bria-ai
Image generation, photo editing, background removal — transparent PNG images, cutouts, ecommerce packshots, product catalogs, lifestyle shots via Bria.ai. Build store-ready e-commerce catalogs at scale — shadows, product dimensions images, lifestyle scenes, marketplace listing va
Install
npx skills add https://github.com/Bria-AI/bria-skill/tree/main/skills/bria-ai
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install bria-ai-bria-skill@llmmart
git clone https://github.com/Bria-AI/bria-skill.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole bria-ai/bria-skill collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Bria — AI Image Generation, Editing & Background Removal
Commercially safe, royalty-free image generation and editing through 20+ API endpoints. Generate from text, edit with natural language, remove backgrounds, create product shots, and build automated image pipelines.
For additional endpoint details beyond what is documented here, see the Bria API reference for agents.
When to Use This Skill
Use this skill when the user wants to:
- Generate images — "create an image of...", "make me a banner", "generate a hero image", "I need a product photo"
- Edit images — "change the background", "make it look like winter", "add a vase to the table", "remove the person"
- Remove/replace backgrounds — "make the background transparent", "cut out the product", "replace with a studio background"
- Product photography — "create a lifestyle shot", "place this product in a kitchen scene", "e-commerce packshot"
- Enhance/transform — "upscale this image", "make it higher resolution", "restyle as oil painting", "change the lighting"
- Batch/pipeline — "generate 10 product images", "process all these images", "remove backgrounds in bulk"
This skill handles the full spectrum of AI image operations. If the user mentions images, photos, visuals, or any visual content creation — use this skill.
What You Can Build
- E-commerce product catalog — Generate product photos, remove backgrounds for transparent PNGs, place products in lifestyle scenes (kitchen, office, outdoor), create packshots with consistent style
- Landing page visuals — Generate hero images, abstract tech backgrounds, team photos, and section illustrations — all matching your brand aesthetic
- Social media content — Instagram posts (1:1), Stories/Reels (9:16), LinkedIn banners (16:9), ad creatives — batch-generate variants for A/B testing
- Marketing campaign assets — Seasonal transformations (summer→winter), restyle product shots for different markets, create localized visuals at scale
- Photo restoration pipeline — Restore old damaged photos, colorize black & white images, upscale low-res photos to 4x, enhance quality automatically
- Brand asset toolkit — Remove backgrounds from logos, blend artwork onto products (t-shirts, mugs), create consistent product photography across your entire catalog
- AI-powered design workflows — Chain operations: generate→edit→remove background→place in scene→upscale — all automated through API pipelines
Setup — Authentication
Before making any API call, you need a valid Bria access token.
Step 1: Check for existing credentials
if [ -f ~/.bria/credentials ]; then
BRIA_ACCESS_TOKEN=$(grep '^access_token=' "$HOME/.bria/credentials" | cut -d= -f2-)
BRIA_API_KEY=$(grep '^api_token=' "$HOME/.bria/credentials" | cut -d= -f2-)
fi
if [ -z "$BRIA_ACCESS_TOKEN" ]; then
echo "NO_CREDENTIALS"
elif [ -n "$BRIA_API_KEY" ]; then
echo "READY"
else
echo "CREDENTIALS_FOUND"
fi
If the output is READY, skip straight to making API calls — no introspection needed.
If the output is CREDENTIALS_FOUND, skip to Step 3.
If the output is NO_CREDENTIALS, proceed to Step 2.
Step 2: Authenticate via device authorization
Start the device authorization flow:
2a. Request a device code:
DEVICE_RESPONSE=$(curl -s -X POST "https://engine.prod.bria-api.com/v2/auth/device/authorize" \
-H "Content-Type: application/json")
echo "$DEVICE_RESPONSE"
Parse the response fields:
device_code— used to poll for the token (keep this, don't show to user)user_code— the code the user must enter (e.g.BRIA-XXXX)interval— seconds between poll attempts
2b. Show the user a single sign-in link. Tell them exactly this — nothing more:
Connect your Bria account: Click here to sign in Your code is — it's already filled in.
Do NOT show two links. Do NOT show the raw URL separately. Do NOT use verification_uri from the API response. Keep it to one clickable link.
2c. Poll for the token. After showing the user the code, immediately start polling. Try up to 60 times with the given interval (default 5 seconds):
for i in $(seq 1 60); do
TOKEN_RESPONSE=$(curl -s -X POST "https://engine.prod.bria-api.com/v2/auth/token" \
-d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
-d "device_code=$DEVICE_CODE")
ACCESS_TOKEN=$(printf '%s' "$TOKEN_RESPONSE" | sed -n 's/.*"access_token" *: *"\([^"]*\)".*/\1/p')
if [ -n "$ACCESS_TOKEN" ]; then
BRIA_ACCESS_TOKEN="$ACCESS_TOKEN"
REFRESH_TOKEN=$(printf '%s' "$TOKEN_RESPONSE" | sed -n 's/.*"refresh_token" *: *"\([^"]*\)".*/\1/p')
mkdir -p ~/.bria
printf 'access_token=%s\nrefresh_token=%s\n' "$BRIA_ACCESS_TOKEN" "$REFRESH_TOKEN" > "$HOME/.bria/credentials"
echo "AUTHENTICATED"
break
fi
sleep 5
done
If the output contains AUTHENTICATED, proceed to Step 3. Otherwise the code expired — start over from Step 2a.
Do not proceed with any API call until authentication is confirmed.
Step 3: Verify billing status and resolve API key
Introspect the bearer token to check billing status and obtain the real API key for Bria API calls:
INTROSPECT=$(curl -s -X POST "https://engine.prod.bria-api.com/v2/auth/token/introspect" \
-d "token=$BRIA_ACCESS_TOKEN")
BILLING_STATUS=$(printf '%s' "$INTROSPECT" | sed -n 's/.*"billing_status" *: *"\([^"]*\)".*/\1/p')
if [ "$BILLING_STATUS" = "blocked" ]; then
BILLING_MSG=$(printf '%s' "$INTROSPECT" | sed -n 's/.*"billing_message" *: *"\([^"]*\)".*/\1/p')
echo "BILLING_ERROR: $BILLING_MSG"
fi
ACTIVE=$(printf '%s' "$INTROSPECT" | sed -n 's/.*"active" *: *\([^,}]*\).*/\1/p' | tr -d ' ')
if [ "$ACTIVE" = "false" ]; then
# Clear stale tokens so re-auth starts fresh (credentials file is re-created in Step 2c)
printf '' > "$HOME/.bria/credentials"
echo "TOKEN_EXPIRED"
fi
BRIA_API_KEY=$(printf '%s' "$INTROSPECT" | sed -n 's/.*"api_token" *: *"\([^"]*\)".*/\1/p')
if [ -n "$BRIA_API_KEY" ]; then
grep -v '^api_token=' "$HOME/.bria/credentials" > "$HOME/.bria/credentials.tmp" 2>/dev/null || true
printf 'api_token=%s\n' "$BRIA_API_KEY" >> "$HOME/.bria/credentials.tmp"
mv "$HOME/.bria/credentials.tmp" "$HOME/.bria/credentials"
fi
Interpret the output:
- If it prints
BILLING_ERROR: ...— relay the message to the user exactly as shown and stop. Do not make any API calls. - If it prints
TOKEN_EXPIRED— the session is no longer valid. Tell the user their session expired and restart from Step 2. - Otherwise,
BRIA_API_KEYnow contains the real API key and is cached for future calls. Proceed to the next section.
Core Capabilities
| Need | Capability | Use Case |
|---|---|---|
| Generate images from text | FIBO Generate | Hero images, product shots, illustrations, social media images, banners |
| Edit images by text instruction | FIBO-Edit | Change colors, modify objects, transform scenes |
| Combine 2–4 images in one edit | FIBO-Edit multi-reference | Put the outfit, product, logo, style, or background of one image into another |
| Edit image region with mask | GenFill/Erase | Precise inpainting, add/replace specific regions |
| Add/Replace/Remove objects | Text-based editing | Add vase, replace apple with pear, remove table |
| Remove background (transparent PNG) | RMBG-2.0 | Extract subjects for overlays, logos, cutouts |
| Turn a finished ad back into layers | Ad Delayer | make a shipped creative editable, resize or localise an ad |
| Replace/blur/erase background | Background ops | Change, blur, or remove backgrounds |
| Expand/outpaint images | Outpainting | Extend boundaries, change aspect ratios |
| Upscale image resolution | Super Resolution | Increase resolution 2x or 4x |
| Enhance image quality | Enhancement | Improve lighting, colors, details |
| Restyle images | Restyle | Oil painting, anime, cartoon, 3D render |
| Change lighting | Relight | Golden hour, spotlight, dramatic lighting |
| Change season | Reseason | Spring, summer, autumn, winter |
| Composite/blend images | Image Blending | Apply textures, logos, merge images |
| Restore old photos | Restoration | Fix old/damaged photos |
| Colorize images | Colorization | Add color to B&W, or convert to B&W |
| Sketch to photo | Sketch2Image | Convert drawings to realistic photos |
| Product cutout | Product Cutout | Clean transparent PNG from a raw product photo |
| Product packshot | Product Packshot | Standardized 2000×2000 shot on a solid/clean background |
| Product shadow | Product Shadow | Add a realistic drop or float shadow to a cutout |
| Create product lifestyle shots | Lifestyle Shot | Place products in scenes for e-commerce |
| Integrate products into scenes | Product Integrate | Embed products at exact coordinates |
| Put a product in someone's hands | Product Holding | Person + product photo → person naturally holding/carrying it |
| Put garments on a model | Virtual Try-On | Person + garment photo(s) → person wearing them |
| Add dimension callouts to products | Product Dimensions | Marketplace-style measurement images with size/weight/capacity labels |
| Build a full product catalog | Catalog Pipeline | Batch a folder of photos → packshots, dimensions, lifestyle, marketplace variants |
How to Call Any Endpoint
Use bria_call for all API calls. It handles URL passthrough, local file base64 encoding, JSON construction, API call, and async polling in a single function call. The API key is auto-loaded from ~/.bria/credentials.
First, source the helper script at references/code-examples/bria_client.sh (resolve relative to this skill's directory).
source <SKILL_DIR>/references/code-examples/bria_client.sh
# Generate (no image input — pass empty string)
RESULT=$(bria_call /v2/image/generate "" '"prompt": "your description", "aspect_ratio": "16:9", "sync": true')
# Remove background
RESULT=$(bria_call /v2/image/edit/remove_background "/path/to/local/image.png")
# Replace background
RESULT=$(bria_call /v2/image/edit/replace_background "https://example.com/img.jpg" '"prompt": "sunset beach"')
# Edit image (uses images array — pass --key images)
RESULT=$(bria_call /v2/image/edit "/path/to/image.png" --key images '"instruction": "make it look warmer"')
# Edit with reference images — each --image adds the next one, in order
RESULT=$(bria_call /v2/image/edit "https://example.com/man.jpg" --key images \
--image "https://example.com/santa.png" \
'"instruction": "dress the man in image 1 in the santa outfit from image 2"')
# Upscale (`desired_increase` is 2 or 4 — no other value. Transparency is preserved by default)
RESULT=$(bria_call /v2/image/edit/increase_resolution "https://example.com/img.jpg" '"desired_increase": 4')
# Product cutout → transparent PNG (use --key file for a local image)
CUTOUT=$(bria_call /v1/product/cutout "/path/to/raw.jpg" --key file)
# Packshot on white from the cutout URL
RESULT=$(bria_call /v1/product/packshot "$CUTOUT" --key image_url '"background_color": "#FFFFFF"')
# Lifestyle shot
RESULT=$(bria_call /v1/product/lifestyle_shot_by_text "/path/to/product.png" '"scene_description": "modern kitchen countertop"')
# Product holding — person + product photo, no prompt needed
RESULT=$(bria_call /v2/image/edit/product/holding "/path/to/person.jpg" --key person_image \
--array-key product_images --image "https://example.com/product.png" \
'"instruction": "Replace the paper coffee cup in her right hand with the can, logo facing the camera."')
# Virtual try-on — person + garment photo(s), no prompt needed. Send a full outfit together
# to change several items in one call.
RESULT=$(bria_call /v2/image/edit/product/virtual-tryon "/path/to/person.jpg" --key person_image \
--array-key garment_images --image "https://example.com/blazer.png" \
--image "https://example.com/trousers.png" \
'"instruction": "He wears the navy blazer over the white t-shirt he already has, and the grey tailored trousers instead of his jeans."')
# Product dimensions — auto-removes background, draws measurement callouts.
# Dual cm / in labels: repeat each dimension with the same name+position in both
# units and set "units_display": "dual_slash" so they merge into one "12 cm / 4.7 in" label.
RESULT=$(bria_call /v2/image/edit/product_dimensions "/path/to/product.png" \
'"style": "default", "units_display": "dual_slash", "dimensions": [{"name": "height", "value": 12, "unit": "cm", "position": "left"}, {"name": "height", "value": 4.7, "unit": "in", "position": "left"}, {"name": "width_bottom", "value": 6, "unit": "cm", "position": "bottom"}, {"name": "width_bottom", "value": 2.4, "unit": "in", "position": "bottom"}], "title": "Gummies Bottle", "capacity": {"value": 500, "unit": "ml"}, "weight": {"value": 250, "unit": "g", "label": "Net Weight"}')
echo "$RESULT"
Calling convention: bria_call <endpoint> <image_or_empty> [--key <json_key>] [extra JSON fields...]
- Pass a URL, local file path, or
""(empty) for endpoints without image input - Use
--key imageswhen the endpoint expects animagesarray instead ofimage - Add
--image <url_or_path>once per extra reference image (--key images, up to 4 in total). Order is preserved: the positional image is "image 1", the first--imageis "image 2", … - Use
--array-key <name>when an endpoint keys the main image and its references separately (e.g.person_image+product_images/garment_images): the positional image goes under--key, and every--imagegoes into the--array-keyarray instead - Extra JSON fields are appended as key-value pairs:
'"key": "value"' - Returns the result image URL on success, or prints an error to stderr
Editing with several images (2–4): reach for a second image when the look the user wants already exists as a picture — a specific outfit, product, logo, or scene — instead of something you can describe in words. Put the image being edited first, references after it, and address them by position in the instruction: "dress the man in image 1 in the santa outfit from image 2". Say what each reference contributes ("the background of image 3"), in plain prose. A single-image edit needs no positional wording: "change the mug color to red".
Generation options: Aspect ratios 1:1, 16:9, 4:3, 9:16, 3:4. Resolution 1MP (default) or 4MP (more detail, +30s). Pass "sync": true for a single generated image. /v2/image/edit (instruction-based FIBO-Edit) is the other way round — it always answers with a status_url you poll, and "sync": true there fails with a gateway
timeout. Product Holding and Virtual Try-On accept "sync": true if you'd rather wait for the final image than poll — bria_call polls for you either way, so this only matters if you're calling the API directly.
Advanced: For precise control over generation, use the vgl skill for structured VGL JSON prompts instead of natural language.
See API Endpoints Reference for full parameter documentation on all 20+ endpoints.
Product Catalog Pipeline (batch)
When the user wants listing-ready imagery for physical products at scale — "build a product catalog from ./products", "turn this folder of photos into a catalog", "make these Amazon/Shopify/Etsy compliant" — use the bundled driver instead of calling endpoints one by one. It is zero-config: with no arguments it reads ./products and writes ./catalog.
python3 <SKILL_DIR>/references/code-examples/build_catalog.py
Per product it runs cutout → packshot + lifestyle scenes (+ dimensions) → marketplace variants and writes cutout.png, packshot.jpg, lifestyle_N.jpg, dimensions.png (when measurements are available), per-channel variants, and a listing.json copy scaffold. Override only what you need:
python3 <SKILL_DIR>/references/code-examples/build_catalog.py \
--input ./photos --output ./store \
--scenes "clean marble surface, soft studio light|cozy wooden desk, warm morning light" \
--variants amazon,shopify --dims ./dims.json
Dimensions need real measurements — ask, don't guess. With no measurements file and no --no-dims, the driver prints NEEDS_DIMENSIONS and exits (code 2). When that happens, stop and ask the user to either provide height/width (and optional weight/capacity) per product — as a dims.json/CSV — or re-run with --no-dims to skip only the dimensions image. Never fabricate sizes. dims.json format:
{ "soap.jpg": {"title": "Hand Wash", "height_cm": 19, "width_cm": 6, "capacity_ml": 250} }
Store measurements in cm — callouts render dual cm / in automatically. Requires pip install requests Pillow.
Marketplace-ready variants
export_variants.py turns one master image (packshot or cutout) into a compliant file per channel — enforcing background, aspect ratio, product fill, and minimum resolution:
python3 <SKILL_DIR>/references/code-examples/export_variants.py \
--input ./catalog/soap/packshot.jpg --output ./catalog/soap --channels amazon,shopify,etsy
| Channel | Aspect | Background | Product fill | Min resolution |
|---|---|---|---|---|
| Amazon (main) | 1:1 | pure white #FFFFFF |
~85% | 1600 px (≥ 3000 ideal) |
| Shopify | 1:1 (+ 4:5) | white / transparent | ~90% | 2048 px |
| Etsy | 5:4 | white / lifestyle | ~85% | 2000 px |
Full rules: Marketplace Presets.
Write the listing copy (SEO)
Bria generates images, not text — you, the agent, write the copy. build_catalog.py writes a listing.json scaffold per product; after the images are generated, view each packshot.jpg and fill it: seo_title (≤ 60 chars, keyword-first), meta_description (≤ 160 chars), description (1–2 paragraphs), bullets (4–6 benefit-led), tags (8–15 keywords). Ground every claim in what's visible — don't invent specs — and fold in any known dimensions.
Prompt Engineering Tips
- Style: "professional product photography" vs "casual snapshot", "flat design illustration" vs "3D rendered"
- Lighting: "soft natural light", "studio lighting", "dramatic shadows"
- Background: "white studio", "gradient", "blurred office", "transparent"
- Composition: "centered", "rule of thirds", "negative space on left for text"
- Quality keywords: "high quality", "professional", "commercial grade", "4K", "sharp focus"
- Negative prompts: "blurry, low quality, pixelated", "text, watermark, logo"
Recipes by Use Case
Hero banner (16:9): "Modern tech startup workspace with developers collaborating, bright natural lighting, clean minimal aesthetic" — include "clean background" or "minimal" for text overlay space
Product photo (1:1): "Professional product photo of [item] on white studio background, soft shadows, commercial photography lighting" — then remove background for transparent PNG
Presentation visual (16:9): "Abstract visualization of data analytics, blue and purple gradient, modern corporate style, clean composition with space for text" — common themes: "abstract technology", "business collaboration", "minimalist geometric patterns"
Instagram post (1:1): "Lifestyle photo of coffee and laptop on wooden desk, morning light, cozy atmosphere"
Story/Reel (9:16): "Vertical product showcase of smartphone, floating in gradient background, tech aesthetic"
Additional Resources
- API Endpoints Reference — Complete endpoint documentation with request/response formats for all 20+ endpoints
- Shell Client (bria_client.sh) — Single-function helper:
bria_callhandles auth, base64, JSON, polling - build_catalog.py — Batch a folder of product photos into a store-ready catalog
- export_variants.py — Turn a master image into Amazon/Shopify/Etsy variants
- Marketplace Presets — Amazon / Shopify / Etsy image specs
- Full API docs for agents (llms.txt) — Agent-ready Bria API reference; use when this skill's summary is not enough
Related Skills
- vgl — Write structured VGL JSON prompts for precise, deterministic control over FIBO image generation
- ad-delayer — Take a finished, flat ad apart into editable layers (background, hero image, logo, copy, CTA)
- image-utils — Classic image manipulation (resize, crop, composite, watermarks) for post-processing
Files (bria-skill)
-
references
-
code-examples
-
bria_client.sh 7 KB
#!/bin/bash # bria_client.sh — Self-contained helper for Bria API calls. # Zero dependencies beyond curl, base64, sed (standard on macOS/Linux). # # Usage: # source bria_client.sh # RESULT=$(bria_call /v2/image/generate "" '"prompt":"a sunset","aspect_ratio":"16:9","sync":true') # RESULT=$(bria_call /v2/image/edit/remove_background "/path/to/image.png") # RESULT=$(bria_call /v2/image/edit/replace_background "https://example.com/img.jpg" '"prompt":"sunset beach"') # RESULT=$(bria_call /v2/image/edit "/path/to/image.png" --key images '"instruction":"make it red"') # RESULT=$(bria_call /v2/image/edit "https://example.com/man.jpg" --key images \ # --image "https://example.com/santa.png" \ # '"instruction":"dress the man in image 1 in the santa outfit from image 2"') # RESULT=$(bria_call /v2/image/edit/product/holding "/path/to/person.jpg" --key person_image \ # --array-key product_images --image "https://example.com/product.png" \ # '"instruction":"logo facing the camera"') # # Each extra --image adds the next reference image, in order: the positional image is "image 1", # the first --image is "image 2", and so on. Only the images array (--key images) takes references # under that same key. Use --array-key <name> when the endpoint keys the main image and its # references separately (e.g. person_image + product_images) — the positional image is sent under # --key, and every --image goes into the --array-key array instead. # # BRIA_API_KEY is auto-loaded from ~/.bria/credentials if not already set. BRIA_API_BASE="${BRIA_API_BASE:-https://engine.prod.bria-api.com}" BRIA_USER_AGENT="BriaSkills/1.4.0" bria_call() { local endpoint image key array_key extra payload result http_code body url status_url poll i img local references=() endpoint="$1"; image="$2"; shift 2 key="image"; array_key=""; extra="" while [ $# -gt 0 ]; do case "$1" in --key) key="$2"; shift 2 ;; --array-key) array_key="$2"; shift 2 ;; --image) references+=("$2"); shift 2 ;; *) extra="${extra:+$extra, }$1"; shift ;; esac done if [ -z "$BRIA_API_KEY" ] && [ -f "$HOME/.bria/credentials" ]; then BRIA_API_KEY=$(grep '^api_token=' "$HOME/.bria/credentials" | cut -d= -f2-) fi [ -z "$BRIA_API_KEY" ] && { echo "ERROR: BRIA_API_KEY not set. Run auth first." >&2; return 1; } # --- Build JSON payload to temp file (safe for large images) --- payload="/tmp/bria_payload_$$.json" if [ -z "$image" ]; then printf '{' > "$payload" elif [ "$key" = "images" ]; then # Written one entry at a time, in argument order: the array position is how the instruction # addresses each image ("image 1", "image 2"), so nothing here may reorder them. printf '{"images": [' > "$payload" i=0 # ${arr[@]+"${arr[@]}"} expands to nothing for an empty array instead of failing under `set -u`. for img in "$image" ${references[@]+"${references[@]}"}; do [ "$i" -gt 0 ] && printf ', ' >> "$payload" if printf '%s' "$img" | grep -qE '^https?://'; then printf '"%s"' "$img" >> "$payload" else [ ! -f "$img" ] && { echo "ERROR: File not found: $img" >&2; return 1; } printf '"' >> "$payload" base64 < "$img" | tr -d '\n' >> "$payload" printf '"' >> "$payload" fi i=$((i + 1)) done printf ']' >> "$payload" elif [ -n "$array_key" ]; then # Main image and its references are keyed separately (e.g. person_image + product_images). if printf '%s' "$image" | grep -qE '^https?://'; then printf '{"%s": "%s"' "$key" "$image" > "$payload" else [ ! -f "$image" ] && { echo "ERROR: File not found: $image" >&2; return 1; } printf '{"%s": "' "$key" > "$payload" base64 < "$image" | tr -d '\n' >> "$payload" printf '"' >> "$payload" fi printf ', "%s": [' "$array_key" >> "$payload" i=0 for img in ${references[@]+"${references[@]}"}; do [ "$i" -gt 0 ] && printf ', ' >> "$payload" if printf '%s' "$img" | grep -qE '^https?://'; then printf '"%s"' "$img" >> "$payload" else [ ! -f "$img" ] && { echo "ERROR: File not found: $img" >&2; return 1; } printf '"' >> "$payload" base64 < "$img" | tr -d '\n' >> "$payload" printf '"' >> "$payload" fi i=$((i + 1)) done printf ']' >> "$payload" elif printf '%s' "$image" | grep -qE '^https?://'; then printf '{"%s": "%s"' "$key" "$image" > "$payload" else [ ! -f "$image" ] && { echo "ERROR: File not found: $image" >&2; return 1; } printf '{"%s": "' "$key" > "$payload" base64 < "$image" | tr -d '\n' >> "$payload" printf '"' >> "$payload" fi if [ -n "$extra" ]; then if [ -z "$image" ]; then printf '%s' "$extra" >> "$payload" else printf ', %s' "$extra" >> "$payload" fi fi printf '}' >> "$payload" # --- API call --- result="/tmp/bria_result_$$.json" http_code=$(curl -s -o "$result" -w '%{http_code}' -X POST \ "${BRIA_API_BASE}${endpoint}" \ -H "api_token: $BRIA_API_KEY" \ -H "Content-Type: application/json" \ -H "User-Agent: $BRIA_USER_AGENT" \ -d @"$payload") body=$(cat "$result") rm -f "$payload" "$result" # --- Error handling --- case "$http_code" in 401) echo "ERROR 401: API key invalid. Delete ~/.bria/credentials and re-authenticate." >&2; return 1 ;; 403) echo "ERROR 403: Billing/quota issue. Visit https://platform.bria.ai/pricing" >&2; echo "$body" >&2; return 1 ;; 5*) echo "ERROR $http_code: Server error. Try again shortly." >&2; return 1 ;; esac if [ "${http_code:-0}" -ge 400 ] 2>/dev/null; then echo "ERROR $http_code: $body" >&2; return 1 fi # --- Extract result URL (sync response) --- url=$(printf '%s' "$body" | sed -n 's/.*"result_url" *: *"\([^"]*\)".*/\1/p') [ -n "$url" ] && { echo "$url"; return 0; } url=$(printf '%s' "$body" | sed -n 's/.*"image_url" *: *"\([^"]*\)".*/\1/p') [ -n "$url" ] && { echo "$url"; return 0; } # Multi-image results (e.g. product_dimensions output_format=dual) use "image_urls": [...]. url=$(printf '%s' "$body" | sed -n 's/.*"image_urls" *: *\[ *"\([^"]*\)".*/\1/p') [ -n "$url" ] && { echo "$url"; return 0; } # --- Async: poll status_url --- status_url=$(printf '%s' "$body" | sed -n 's/.*"status_url" *: *"\([^"]*\)".*/\1/p') if [ -n "$status_url" ]; then i=0 while [ "$i" -lt 30 ]; do sleep 3 poll=$(curl -s "$status_url" \ -H "api_token: $BRIA_API_KEY" \ -H "User-Agent: $BRIA_USER_AGENT") if printf '%s' "$poll" | grep -qE '"status" *: *"(ERROR|FAILED)"'; then echo "ERROR: Job failed. Response: $poll" >&2; return 1 fi url=$(printf '%s' "$poll" | sed -n 's/.*"result_url" *: *"\([^"]*\)".*/\1/p') [ -z "$url" ] && url=$(printf '%s' "$poll" | sed -n 's/.*"image_url" *: *"\([^"]*\)".*/\1/p') [ -z "$url" ] && url=$(printf '%s' "$poll" | sed -n 's/.*"image_urls" *: *\[ *"\([^"]*\)".*/\1/p') [ -n "$url" ] && { echo "$url"; return 0; } i=$((i + 1)) done echo "ERROR: Polling timed out after 90 seconds" >&2 return 1 fi echo "$body" } -
build_catalog.py 11.6 KB
#!/usr/bin/env python3 """ build_catalog.py — Batch: a folder of raw product photos -> a store-ready catalog. For each image it runs the Bria product pipeline: cutout -> packshot (white) + lifestyle scenes (+ dimensions if measurements given) -> marketplace-ready variants (via export_variants.py) Usage: python3 build_catalog.py --input ./products --output ./catalog \ --scenes "marble bathroom shelf, soft light|bright kitchen counter, morning sun" \ --variants amazon,shopify,etsy --dims ./dims.json Auth: BRIA_API_KEY from env or ~/.bria/credentials. Deps: pip install requests Pillow """ import argparse, base64, io, json, os, sys, time import requests from PIL import Image HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) import export_variants as ev # noqa: E402 BASE = os.environ.get("BRIA_API_BASE", "https://engine.prod.bria-api.com") IMG_EXT = (".jpg", ".jpeg", ".png", ".webp") # Sensible zero-config defaults so "create a catalog for each product" just works. DEFAULT_SCENES = ("on a clean minimal surface with soft natural light and a subtle shadow" "|on a warm wooden surface with a small plant nearby, soft daylight") def api_key(): k = os.environ.get("BRIA_API_KEY") if k: return k cred = os.path.expanduser("~/.bria/credentials") if os.path.exists(cred): for line in open(cred): if line.startswith("api_token="): return line.strip().split("=", 1)[1] sys.exit("ERROR: BRIA_API_KEY not set and no ~/.bria/credentials. Authenticate first.") KEY = None def H(): return {"api_token": KEY, "Content-Type": "application/json", "User-Agent": "BriaSkills/1.4.0"} def post(path, payload, tries=3): for t in range(tries): r = requests.post(f"{BASE}{path}", headers=H(), json=payload, timeout=180) if r.status_code in (200, 202): return r.json() print(f" ! {path} -> {r.status_code}: {r.text[:180]}") time.sleep(4) raise RuntimeError(f"failed {path}: {r.status_code}") def b64_resized(path, maxside=1800): im = Image.open(path).convert("RGB") w, h = im.size s = min(1.0, maxside / max(w, h)) if s < 1.0: im = im.resize((int(w * s), int(h * s)), Image.LANCZOS) buf = io.BytesIO(); im.save(buf, format="JPEG", quality=90) return base64.b64encode(buf.getvalue()).decode() def result_url(j): if j.get("result_url"): return j["result_url"] res = j.get("result") if isinstance(res, list) and res: first = res[0] return first[0] if isinstance(first, list) else first if isinstance(res, dict): u = res.get("image_url") or res.get("url") if u: return u # Multi-image results (e.g. product_dimensions output_format=dual) use image_urls. urls = res.get("image_urls") if isinstance(urls, list) and urls: return urls[0] return None def poll(status_url, tries=40): for _ in range(tries): j = requests.get(status_url, headers=H(), timeout=60).json() if str(j.get("status", "")).upper() in ("FAILED", "ERROR"): raise RuntimeError(f"job failed: {json.dumps(j)[:200]}") u = result_url(j) if u: return u time.sleep(3) raise RuntimeError("timeout polling") def download(url, dest): r = requests.get(url, timeout=180); r.raise_for_status() # Bria's packshot/lifestyle endpoints return PNG bytes. When the caller wants a # .jpg, re-encode so the file's content matches its extension (flatten any alpha # onto white — packshots are already on a white background). For .png keep the # raw bytes so cutout/dimensions transparency is preserved. if dest.lower().endswith((".jpg", ".jpeg")): im = Image.open(io.BytesIO(r.content)) if im.mode in ("RGBA", "LA", "P"): im = im.convert("RGBA") bg = Image.new("RGB", im.size, (255, 255, 255)) bg.paste(im, mask=im.split()[-1]) im = bg else: im = im.convert("RGB") im.save(dest, format="JPEG", quality=92) else: with open(dest, "wb") as f: f.write(r.content) print(f" saved {os.path.relpath(dest)}") def load_dims(path): """Load measurements from .json ({filename: {...}}) or .csv (header row with filename,title,height_cm,width_cm,weight_g,capacity_ml).""" if path.lower().endswith(".csv"): import csv out = {} with open(path, newline="") as f: for row in csv.DictReader(f): key = (row.get("filename") or row.get("file") or "").strip() if not key: continue entry = {} if row.get("title"): entry["title"] = row["title"].strip() for k in ("height_cm", "width_cm", "weight_g", "capacity_ml"): v = (row.get(k) or "").strip() if v: entry[k] = float(v) out[key] = entry return out return json.load(open(path)) def dims_payload(entry): # Dual-unit callouts: each linear dimension is emitted twice (cm + in) with the # same name+position so Bria merges them into one "19 cm / 7.5 in" label. dims = [] def add(name, cm, position): inches = round(float(cm) / 2.54, 1) dims.append({"name": name, "value": float(cm), "unit": "cm", "position": position}) dims.append({"name": name, "value": inches, "unit": "in", "position": position}) if entry.get("height_cm"): add("height", entry["height_cm"], "left") if entry.get("width_cm"): add("width_bottom", entry["width_cm"], "bottom") p = {"style": "default", "background": "white", "output_format": "png", "output_size": 1600, "dimensions": dims, "units_display": "dual_slash"} if entry.get("title"): p["title"] = entry["title"] if entry.get("weight_g"): p["weight"] = {"value": entry["weight_g"], "unit": "g", "label": "Net Weight"} if entry.get("capacity_ml"): p["capacity"] = {"value": entry["capacity_ml"], "unit": "ml"} return p def export_variants(packshot_path, out_dir, channels): im = ev.load_rgba(packshot_path) sprite = im.crop(ev.product_bbox(im)) for ch in channels: if ch not in ev.PRESETS: continue aw, ah, fill, ms = ev.PRESETS[ch] out, ext = ev.make_variant(sprite, ev.target_size(aw, ah, ms), fill, "white") dest = os.path.join(out_dir, f"{ch}.{ext}") out.save(dest, **({"quality": 92} if ext == "jpg" else {})) print(f" variant {ch}: {os.path.relpath(dest)} ({out.width}x{out.height})") def main(): global KEY ap = argparse.ArgumentParser(description="Folder of product photos -> store-ready catalog. Runs with zero args using ./products -> ./catalog.") ap.add_argument("--input", default="./products", help="folder of product photos (default ./products)") ap.add_argument("--output", default="./catalog", help="output folder (default ./catalog)") ap.add_argument("--scenes", default=DEFAULT_SCENES, help="pipe-separated lifestyle scenes (has sensible defaults)") ap.add_argument("--variants", default="amazon,shopify,etsy", help="marketplace variants to export") ap.add_argument("--dims", default=None, help="measurements file (.json or .csv); auto-detects ./dims.json") ap.add_argument("--no-dims", action="store_true", help="skip dimension images without asking") args = ap.parse_args() KEY = api_key() scenes = [s.strip() for s in args.scenes.split("|") if s.strip()] channels = [c.strip() for c in args.variants.split(",") if c.strip()] # Resolve measurements: explicit --dims, else auto-detect ./dims.json or <input>/dims.json. # --no-dims means no dimension images at all, so don't auto-detect a measurements file either. dims_path = args.dims if args.no_dims: dims_path = None elif not dims_path: for cand in ("dims.json", "dims.csv", os.path.join(args.input, "dims.json"), os.path.join(args.input, "dims.csv")): if os.path.exists(cand): dims_path = cand print(f"(using measurements from {cand})") break dims = load_dims(dims_path) if dims_path else {} if not dims and not args.no_dims: # Do NOT silently skip. Signal that measurements are needed so the caller can ask the user. print("NEEDS_DIMENSIONS: no measurements found. To include dimension images, re-run with " "--dims <file.json|file.csv>. To proceed without them, re-run with --no-dims.") sys.exit(2) imgs = sorted(f for f in os.listdir(args.input) if f.lower().endswith(IMG_EXT)) if not imgs: sys.exit(f"No images in {args.input}") print(f"Building catalog for {len(imgs)} product(s) -> {args.output}") manifest = {} for fn in imgs: slug = os.path.splitext(fn)[0] src = os.path.join(args.input, fn) out_dir = os.path.join(args.output, slug) os.makedirs(out_dir, exist_ok=True) print(f"== {slug} ==") files = {} b64 = b64_resized(src) cut_url = result_url(post("/v1/product/cutout", {"file": b64})) download(cut_url, os.path.join(out_dir, "cutout.png")); files["cutout"] = "cutout.png" ps_url = result_url(post("/v1/product/packshot", {"image_url": cut_url, "background_color": "#FFFFFF"})) ps_path = os.path.join(out_dir, "packshot.jpg") download(ps_url, ps_path); files["packshot"] = "packshot.jpg" if fn in dims or slug in dims: entry = dims.get(fn) or dims.get(slug) j = post("/v2/image/edit/product_dimensions", {"image": b64, **dims_payload(entry)}) u = result_url(j) or poll(j.get("status_url") or f"{BASE}/v2/status/{j.get('request_id')}") download(u, os.path.join(out_dir, "dimensions.png")); files["dimensions"] = "dimensions.png" for i, scene in enumerate(scenes, 1): j = post("/v1/product/lifestyle_shot_by_text", { "image_url": cut_url, "scene_description": scene, "mode": "high_control", "placement_type": "automatic_aspect_ratio", "aspect_ratio": "1:1", "num_results": 1, "sync": True, "optimize_description": True}) u = result_url(j) if u: download(u, os.path.join(out_dir, f"lifestyle_{i}.jpg")) files[f"lifestyle_{i}"] = f"lifestyle_{i}.jpg" export_variants(ps_path, out_dir, channels) # Listing-copy scaffold. Bria generates images, not text — the agent (LLM) # fills the SEO fields below by viewing packshot.jpg. See SKILL.md "Write listing copy". entry = dims.get(fn) or dims.get(slug) or {} listing = { "sku": slug, "title": entry.get("title"), "seo_title": None, # <= 60 chars, keyword-first "meta_description": None, # <= 160 chars "description": None, # 1-2 paragraph product description "bullets": [], # 4-6 benefit-led bullet points "tags": [], # search keywords "dimensions": entry or None, "images": files, } with open(os.path.join(out_dir, "listing.json"), "w") as f: json.dump(listing, f, indent=2) files["listing"] = "listing.json" manifest[slug] = files with open(os.path.join(args.output, "catalog.json"), "w") as f: json.dump(manifest, f, indent=2) print(f"DONE — catalog manifest at {os.path.join(args.output, 'catalog.json')}") if __name__ == "__main__": main() -
export_variants.py 4.1 KB
#!/usr/bin/env python3 """ export_variants.py — Turn one master product image (packshot or transparent cutout) into marketplace-ready variants: correct background, aspect ratio, product fill ratio, and minimum resolution for Amazon, Shopify, and Etsy. Usage: python3 export_variants.py --input packshot.jpg --output ./out --channels amazon,shopify,etsy python3 export_variants.py --input cutout.png --output ./out --channels shopify --bg transparent Dependencies: Pillow (pip install Pillow) """ import argparse import os from PIL import Image # channel -> (aspect_w, aspect_h, product_fill, min_shortest_side_px) PRESETS = { "amazon": (1, 1, 0.85, 2000), # main image: pure white, product ~85% of frame, >=1600 (2000 safe) "shopify": (1, 1, 0.90, 2048), # square, consistent padding "etsy": (5, 4, 0.85, 2000), # 5:4 landscape thumbnails } WHITE = (255, 255, 255, 255) def load_rgba(path): return Image.open(path).convert("RGBA") def product_bbox(im, white_thresh=244): """Bounding box of the product. Uses alpha if present, else near-white background detection.""" alpha = im.getchannel("A") if alpha.getextrema()[0] < 255: # real transparency exists box = alpha.getbbox() if box: return box # Flatten on white, find pixels that differ from white rgb = Image.new("RGB", im.size, (255, 255, 255)) rgb.paste(im, mask=im.getchannel("A")) gray = rgb.convert("L") mask = gray.point(lambda p: 255 if p < white_thresh else 0) box = mask.getbbox() return box or (0, 0, im.width, im.height) def target_size(aw, ah, min_side): """Canvas size with the SHORTEST side == min_side, matching aspect aw:ah.""" if aw >= ah: # landscape or square -> height is the short side h = min_side w = round(min_side * aw / ah) else: # portrait -> width is the short side w = min_side h = round(min_side * ah / aw) return w, h def make_variant(sprite, canvas_wh, fill, bg): cw, ch = canvas_wh sw, sh = sprite.size # scale so the product spans `fill` of the frame in its dominant dimension scale = min(cw * fill / sw, ch * fill / sh) nw, nh = max(1, round(sw * scale)), max(1, round(sh * scale)) resized = sprite.resize((nw, nh), Image.LANCZOS) if bg == "transparent": canvas = Image.new("RGBA", (cw, ch), (0, 0, 0, 0)) canvas.paste(resized, ((cw - nw) // 2, (ch - nh) // 2), resized) return canvas, "png" color = (255, 255, 255, 255) if bg == "white" else _hex(bg) canvas = Image.new("RGBA", (cw, ch), color) canvas.paste(resized, ((cw - nw) // 2, (ch - nh) // 2), resized) return canvas.convert("RGB"), "jpg" def _hex(s): s = s.lstrip("#") return (int(s[0:2], 16), int(s[2:4], 16), int(s[4:6], 16), 255) def main(): ap = argparse.ArgumentParser() ap.add_argument("--input", required=True, help="master image (packshot or transparent cutout)") ap.add_argument("--output", required=True, help="output directory") ap.add_argument("--channels", default="amazon,shopify,etsy", help="comma list: amazon,shopify,etsy") ap.add_argument("--bg", default="white", help="white | transparent | #RRGGBB (default white)") ap.add_argument("--fill", type=float, default=None, help="override product fill ratio (0-1)") args = ap.parse_args() os.makedirs(args.output, exist_ok=True) im = load_rgba(args.input) sprite = im.crop(product_bbox(im)) stem = os.path.splitext(os.path.basename(args.input))[0] for ch in [c.strip() for c in args.channels.split(",") if c.strip()]: if ch not in PRESETS: print(f"! unknown channel: {ch} (skipping)") continue aw, ah, fill, min_side = PRESETS[ch] fill = args.fill if args.fill is not None else fill canvas_wh = target_size(aw, ah, min_side) out, ext = make_variant(sprite, canvas_wh, fill, args.bg) dest = os.path.join(args.output, f"{stem}_{ch}.{ext}") save_kwargs = {"quality": 92} if ext == "jpg" else {} out.save(dest, **save_kwargs) print(f" {ch}: {dest} ({out.width}x{out.height})") if __name__ == "__main__": main()
-
-
api-endpoints.md 29 KB
# Bria.ai API Reference ## Base URL & Authentication **Base URL:** `https://engine.prod.bria-api.com` **Authentication:** Include these headers in all requests: ``` api_token: YOUR_BRIA_API_KEY Content-Type: application/json User-Agent: BriaSkills/<version> ``` > **Required:** Always include the `User-Agent: BriaSkills/<version>` header (where `<version>` is the current skill version from `package.json`, e.g. `BriaSkills/1.4.0`) in every API call, including status polling requests. --- ## FIBO - Image Generation ### POST /v2/image/generate Generate images from text prompts using FIBO's structured prompt system. **Request:** ```json { "prompt": "string (required)", "aspect_ratio": "1:1", "resolution": "1MP", "negative_prompt": "string", "seed": null, "style_id": "default" } ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `prompt` | string | required* | Image description (* or use `structured_prompt`) | | `aspect_ratio` | string | "1:1" | "1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9" | | `resolution` | string | "1MP" | Output image resolution. "1MP" or "4MP". "4MP" improves image details, especially for photorealism, but increases latency by ~30 seconds. | | `negative_prompt` | string | - | What to exclude | | `seed` | int | random | For reproducibility | | `style_id` | string | "default" | Named prompt style that shapes how the prompt becomes the image. `"default"` (standard) or `"photoreal"` (tuned for photorealistic results). Optional — omit for the standard style. | | `structured_prompt` | string | - | JSON from previous generation (for refinement). Use with `prompt` to refine, or alone with `seed` to recreate. | | `images` | array | - | Reference image for inspire mode: an array holding one image URL or base64 string | **Input Combinations** — at least one of `prompt`, `images` or `structured_prompt` is required: - `prompt` — Generate from text - `images` — Generate inspired by a reference image - `images` + `prompt` — Generate inspired by image, guided by text - `structured_prompt` + `seed` — Recreate a previous image exactly - `structured_prompt` + `prompt` + `seed` — Refine a previous image with new instructions All combinations support `aspect_ratio`, `negative_prompt`, `seed`, and `style_id`. Note that `"sync": true` cannot be combined with `"resolution": "4MP"` — that pairing is rejected. **Response:** ```json { "request_id": "uuid", "status_url": "https://engine.prod.bria-api.com/v2/status/uuid" } ``` **Completed Result:** ```json { "status": "COMPLETED", "result": { "image_url": "https://...", "structured_prompt": "{...}", "seed": 12345 } } ``` --- ## RMBG-2.0 - Background Removal ### POST /v2/image/edit/remove_background Remove background from image. Returns PNG with transparency. **Request:** ```json { "image": "https://publicly-accessible-image-url" } ``` **Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `image` | string | Source image URL (JPEG, PNG, WEBP) | **Response:** ```json { "request_id": "uuid", "status_url": "https://..." } ``` **Completed Result:** ```json { "status": "COMPLETED", "result": { "image_url": "https://...png" } } ``` --- ## FIBO-Edit - Image Editing ### POST /v2/image/edit Edit an image with a natural language instruction — no mask required. Send one image to change it, or 2–4 images to combine them: the subject from one with an outfit, product, style, or background from another. **Request:** ```json { "images": ["https://source-image-url"], "instruction": "change the mug color to red" } ``` **Multi-reference request.** `images` is ordered, and the instruction addresses each entry by its position — the first is "image 1", the second "image 2", and so on: ```json { "images": ["https://man-image-url", "https://santa-outfit-image-url"], "instruction": "dress the man in image 1 in the santa outfit from image 2", "seed": 1234 } ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `images` | array | required | 1–4 image URLs or base64 data URLs. **Order matters** — the instruction refers to them as "image 1", "image 2", … in the order they are sent | | `instruction` | string | required | Edit instruction in natural language. Refer to additional images by position | | `seed` | int | random | For reproducibility — the same images, instruction and seed reproduce the same result | | `aspect_ratio` | string | - | Output ratio: "1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9". Honored only with 2 or more images. Do not send it on a single-image request — the output follows that image regardless and the response carries a `warning`; to change one image's ratio, use `/v2/image/edit/expand` | | `model_version` | string | - | Deprecated and ignored — the service picks the edit model from the request contents. A value that is sent comes back with a notice in `warning`; omit it | **Writing a multi-reference instruction:** - Put the image being edited first and the references after it. - Say what each reference contributes ("the outfit from image 2", "the background of image 3"), not just that it exists. - Plain prose, plain words — "image 1", "image 2". No brackets, tags, or markup. This endpoint is asynchronous: it answers with `request_id` and `status_url`, which you poll. Do not send `"sync": true` here — an instruction edit takes longer than a single response is allowed to take, so a synchronous request fails with a gateway timeout even though the job itself is fine. **Constraints** — each returns 422 with a readable message: - More than 4 images. Trim the set before sending; the request is refused, not truncated. - A `mask` together with 2 or more images (masked edits are single-image only). - 2 or more images together with a tailored `model_id` or `model_version: FIBO_BBQ` — both of those run on the single-reference model. **Completed Result:** ```json { "status": "COMPLETED", "result": { "image_url": "https://...", "seed": 1234, "structured_prompt": "{...}", "warning": null } } ``` The result also carries the structured instruction the edit was rendered from — named `structured_prompt` on a polled result and `structured_instruction` on an inline `"sync": true` response. Read whichever is present. `warning` is set when a parameter was accepted but not honored — `aspect_ratio` on a single-image request, a supplied `model_version`, or a tuning parameter the serving model does not read. Relay it to the user rather than dropping it. ### POST /v2/image/edit/gen_fill Generate content in a masked region (inpainting). **Request:** ```json { "image": "https://source-image-url", "mask": "https://mask-image-url", "prompt": "what to generate", "mask_type": "manual" } ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `image` | string | required | Source image URL | | `mask` | string | required | Mask URL (white=edit, black=keep) | | `prompt` | string | required | What to generate in masked area | | `mask_type` | string | "manual" | "manual" or "automatic" | **Mask Requirements:** - White pixels (255) = area to edit - Black pixels (0) = area to preserve - Same aspect ratio as source image ### POST /v2/image/edit/erase Remove objects defined by mask. **Request:** ```json { "image": "https://source-image-url", "mask": "https://mask-image-url" } ``` ### POST /v2/image/edit/erase_foreground Remove primary subject and fill with background. **Request:** ```json { "image": "https://source-image-url" } ``` ### POST /v2/image/edit/replace_background Replace background with AI-generated content. **Request:** ```json { "image": "https://source-image-url", "prompt": "new background description" } ``` ### POST /v2/image/edit/blur_background Apply blur effect to image background. **Request:** ```json { "image": "https://source-image-url" } ``` ### POST /v2/image/edit/expand Expand/outpaint an image to extend its boundaries. **Request:** ```json { "image": "base64-string-or-url", "aspect_ratio": "16:9", "prompt": "optional description for new content" } ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `image` | string | required | Source image URL or base64 string | | `aspect_ratio` | string \| float | - | Target ratio: "1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", or a float. Omit it and pass `canvas_size` instead | | `prompt` | string | - | Optional - describe content to generate | ### POST /v2/image/edit/enhance Enhance image quality (lighting, colors, details). **Request:** ```json { "image": "https://source-image-url" } ``` ### POST /v2/image/edit/increase_resolution Upscale image resolution. **Request:** ```json { "image": "https://source-image-url", "desired_increase": 4, "preserve_alpha": true } ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `image` | string | required | Source image URL | | `desired_increase` | int | 2 | Upscale factor: 2 or 4 | | `preserve_alpha` | bool | true | Preserve transparency. Set `true` when input has an alpha channel — the API upscales and recombines the alpha server-side, so you don't need to handle it client-side. | ### POST /v1/product/cutout Remove the background from a product photo → clean transparent PNG. The `/v1/product/*` endpoints take `image_url` (URL) or `file` (base64). **Request:** ```json { "image_url": "https://…/raw.jpg" } ``` Response: `{ "result_url": "https://…png" }` (synchronous). ### POST /v1/product/packshot Standardized 2000×2000 packshot on a solid/clean background. | Parameter | Type | Notes | |-----------|------|-------| | `image_url` / `file` | string | Product image (a cutout is recommended) | | `background_color` | string | Hex like `#FFFFFF`, or `transparent` | | `sku` | string | Optional label/id | Response: `{ "result_url": "…" }` (synchronous). ### POST /v1/product/shadow Add a realistic shadow to a product cutout. | Parameter | Type | Notes | |-----------|------|-------| | `image_url` / `file` | string | Product cutout | | `type` | string | `regular` (drop) or `float` (elliptical) | | `background_color` | string | Hex or `transparent` | | `shadow_intensity` | int | 0–100 (approx) | Response: `{ "result_url": "…" }` (synchronous). ### POST /v1/product/lifestyle_shot_by_text Place a product in a lifestyle scene using text description. **Request:** ```json { "file": "BASE64_ENCODED_IMAGE", "scene_description": "modern kitchen countertop, natural lighting", "placement_type": "automatic" } ``` **Parameters:** | Parameter | Type | Notes | |-----------|------|-------| | `image_url` / `file` | string | Product (cutout recommended) | | `scene_description` | string | Environment + lighting + mood | | `mode` | string | `base`, `high_control` (recommended), `fast` | | `placement_type` | string | `automatic`, `automatic_aspect_ratio`, `manual_placement`, `custom_coordinates`, `manual_padding`, `original` | | `aspect_ratio` | string | e.g. `1:1`, `4:5`, `16:9` (with `automatic_aspect_ratio`) | | `num_results` | int | Number of variations | | `sync` | bool | `true` returns results inline | | `optimize_description` | bool | Let Bria refine the prompt | Response: `{ "result": [[ "image_url", "seed", "session_id" ], …] }` — extract `result[0][0]`. ### POST /v1/product/lifestyle_shot_by_image Same as `lifestyle_shot_by_text`, but the scene comes from a reference background image instead of a text description. | Parameter | Type | Notes | |-----------|------|-------| | `image_url` / `file` | string | Product | | `ref_image_urls` | array | One or more background reference URLs | | `placement_type` | string | see above | | `num_results` | int | variations | Response: `{ "result": [[ "image_url", … ], …] }`. ### POST /v2/image/edit/product/integrate Integrate and embed one or more products into a predefined scene at precise user-defined coordinates. The product is automatically matched to the scene's lighting, perspective, and aesthetics. Products are automatically cut out from their background as part of the pipeline. **Request:** ```json { "scene": "https://scene-image-url", "products": [ { "image": "https://product-image-url", "coordinates": { "x": 100, "y": 200, "width": 300, "height": 400 } } ], "seed": 42 } ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `scene` | string | required | Scene image URL or base64. Accepted formats: jpeg, jpg, png, webp | | `products` | array | required | Array of product objects (1 to N products) | | `products[].image` | string | required | Product image URL or base64. If it has an alpha channel, no cutout is applied; otherwise automatic cutout is applied | | `products[].coordinates` | object | required | Placement and scaling of the product within the scene | | `products[].coordinates.x` | int | required | X-coordinate of the product's top-left corner (pixels) | | `products[].coordinates.y` | int | required | Y-coordinate of the product's top-left corner (pixels) | | `products[].coordinates.width` | int | required | Desired product width in pixels (must not exceed scene dimensions) | | `products[].coordinates.height` | int | required | Desired product height in pixels (must not exceed scene dimensions) | | `seed` | int | random | Seed for deterministic generation | **Response:** ```json { "request_id": "uuid", "result": { "image_url": "https://..." } } ``` **Async Response (202):** ```json { "request_id": "uuid", "status_url": "https://..." } ``` ### POST /v2/image/edit/product/holding Put a product in someone's hands. Send `person_image` and one to three `product_images`, and the endpoint returns the person naturally holding or carrying the product. No prompt is needed — a person photo and a product photo are a complete request. The person's identity, pose, background, and original aspect ratio are preserved, and the product keeps its exact geometry, colours, branding, and label text. Several product references in one call can compose packaging, a second angle, or a companion item into a single shot. Add an optional `instruction` to art-direct a specific shot (e.g. "Replace the paper coffee cup in her right hand with the can, logo facing the camera."). **Request:** ```json { "person_image": "https://person-image-url", "product_images": [ "https://product-image-url" ], "instruction": "Replace the paper coffee cup in her right hand with the can, logo facing the camera." } ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `person_image` | string | required | Photo of the person to edit. URL or base64. Accepted formats: jpeg, jpg, png, webp | | `product_images` | array | required | One to three product images: packaging, a second angle, or a companion item. Each entry is a URL or base64, same as `person_image` | | `instruction` | string | - | Extra direction for this shot. Optional — send only the detail to steer, not a full prompt | | `aspect_ratio` | string | `person_image`'s aspect ratio | `1:1`, `2:3`, `3:2`, `3:4`, `4:3`, `4:5`, `5:4`, `9:16`, `16:9` | | `seed` | int | random | Seed for deterministic generation | | `sync` | bool | false | `true` holds the connection open and returns the final image; `false` returns a `status_url` to poll | | `webhook_url` | string | - | Receive the result via webhook when the async job completes | | `output_type` | string | - | `png` or `jpeg` | | `visual_output_content_moderation` | bool | false | If true, returns 422 on visual output moderation failure | By default (`sync` omitted or `false`) this endpoint is asynchronous: it answers with `request_id` and `status_url`, which you poll. **Async Response (202):** ```json { "request_id": "uuid", "status_url": "https://..." } ``` **Completed Result:** ```json { "status": "COMPLETED", "result": { "image_url": "https://...", "seed": 1234, "structured_prompt": "{...}", "warning": null } } ``` The result also carries the structured prompt the shot was rendered from, in `structured_prompt` (same field name and meaning as on `/v2/image/edit`). `warning` is set when a parameter was accepted but not honored — relay it to the user rather than dropping it. ### POST /v2/image/edit/product/virtual-tryon Put garments on a model. Send `person_image` and one to three `garment_images`, and the endpoint returns the person wearing them. No prompt is needed — a person photo and the garments are a complete request. Garment fidelity carries through: print scale, stripe alignment across seams, collar and closure type, and how a garment reads from behind. The person's identity, pose, background, and original aspect ratio are preserved. Send a full outfit together to change several items in one call instead of stacking edits. Add an optional `instruction` to direct the styling (e.g. "He wears the navy blazer over the white t-shirt he already has, and the grey tailored trousers instead of his jeans."). **Request:** ```json { "person_image": "https://person-image-url", "garment_images": [ "https://garment-image-url" ], "instruction": "He wears the navy blazer over the white t-shirt he already has." } ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `person_image` | string | required | Photo of the person to edit. URL or base64. Accepted formats: jpeg, jpg, png, webp | | `garment_images` | array | required | One to three garment or accessory images to put on the person. Each entry is a URL or base64, same as `person_image` | | `instruction` | string | - | Extra direction for this shot. Optional — send only the detail to steer, not a full prompt | | `aspect_ratio` | string | `person_image`'s aspect ratio | `1:1`, `2:3`, `3:2`, `3:4`, `4:3`, `4:5`, `5:4`, `9:16`, `16:9` | | `seed` | int | random | Seed for deterministic generation | | `sync` | bool | false | `true` holds the connection open and returns the final image; `false` returns a `status_url` to poll | | `webhook_url` | string | - | Receive the result via webhook when the async job completes | | `output_type` | string | - | `png` or `jpeg` | | `visual_output_content_moderation` | bool | false | If true, returns 422 on visual output moderation failure | By default (`sync` omitted or `false`) this endpoint is asynchronous: it answers with `request_id` and `status_url`, which you poll. **Async Response (202):** ```json { "request_id": "uuid", "status_url": "https://..." } ``` **Completed Result:** ```json { "status": "COMPLETED", "result": { "image_url": "https://...", "seed": 1234, "structured_prompt": "{...}", "warning": null } } ``` The result also carries the structured prompt the shot was rendered from, in `structured_prompt` (same field name and meaning as on `/v2/image/edit`). `warning` is set when a parameter was accepted but not honored — relay it to the user rather than dropping it. ### POST /v2/image/edit/product/generate/dimensions Render a marketplace-ready dimension image from a product photo. (The older `/v2/image/edit/product_dimensions` path still works but is deprecated — use this one.) How it works: the background is removed automatically, then measurement callout lines + labels are drawn around the product, with an optional title and optional weight/capacity text. Three visual styles. Useful for e-commerce listings (Amazon-style "dimensions" images). **Request:** ```json { "image": "https://product-image-url", "style": "default", "dimensions": [ {"name": "height", "value": 12, "unit": "cm", "position": "left"}, {"name": "width_bottom", "value": 6, "unit": "cm", "position": "bottom"} ], "title": "Gummies Bottle", "weight": {"value": 250, "unit": "g", "label": "Net Weight"}, "capacity": {"value": 500, "unit": "ml"}, "background": "white", "output_format": "png" } ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `image` | string | required | Product photo URL or base64. Background is removed automatically — no pre-cutout needed | | `dimensions` | array | required | One or more dimension callouts (min 1) | | `dimensions[].name` | string | required | `height`, `width_bottom`, or `width_top` (`length`/`depth` not currently enabled) | | `dimensions[].value` | float | required | Physical measurement value (must be > 0) | | `dimensions[].unit` | string | required | `mm`, `cm`, `m`, `in`, `"` (inches), `ft`, `'` (feet) | | `dimensions[].position` | string | per-name | Callout side: `top`, `bottom`, `left`, `right`. Defaults: height→`left`, width_bottom→`bottom`, width_top→`top` | | `style` | string | required | `default`, `childlike`, or `elegant` | | `units_display` | string | "single" | `single`, `dual_bullet`, `dual_slash`, `dual_parens`. For dual modes, supply two `dimensions` entries with the same `name`+`position` but different `unit` (e.g. `in` and `cm`) — they merge into one dual-unit label | | `background` | string | "white" | `white`, `cream`, `charcoal`, or a hex color (e.g. `#f5f0e8`) | | `title` | string | - | Optional headline above the product (max 80 chars) | | `title_position` | string | "top_center" | `top_left`, `top_center`, `top_right` | | `weight` | object | - | Optional weight callout below the product | | `weight.value` | float | required* | Weight value (> 0) *if `weight` is provided | | `weight.unit` | string | required* | `lb`, `oz`, `g`, `kg` | | `weight.label` | string | "Weight" | `Weight` or `Net Weight` | | `capacity` | object | - | Optional capacity callout below the product | | `capacity.value` | float | required* | Capacity value (> 0) *if `capacity` is provided | | `capacity.unit` | string | required* | `fl_oz`, `ml`, `l`, `qt`, `gal`, `cups` | | `output_format` | string | "png" | `png`, `jpeg`, or `dual` (composite PNG + a transparent overlay-only PNG, returned as two images) | | `output_size` | int | 2200 | Square output edge length in px (256–2200) | | `proportional_lines` | bool | true | Scale each dimension line's length to its measurement (the largest per axis spans the product) | **Async Response (202):** ```json { "request_id": "uuid", "status_url": "https://..." } ``` Poll `status_url` for the result. `dual` output returns two images: `[composite, overlay]`. --- ## Text-Based Object Editing ### POST /v2/image/edit/add_object_by_text Add a new object to an image using natural language. **Request:** ```json { "image": "base64-or-url", "instruction": "Place a red vase with flowers on the table" } ``` ### POST /v2/image/edit/replace_object_by_text Replace an existing object with a new one. **Request:** ```json { "image": "base64-or-url", "instruction": "Replace the red apple with a green pear" } ``` ### POST /v2/image/edit/erase_by_text Remove a specific object by name. **Request:** ```json { "image": "base64-or-url", "object_name": "table" } ``` --- ## Image Transformation ### POST /v2/image/edit/blend Blend/merge images or apply textures. **Request:** ```json { "image": "base64-or-url", "instruction": "Place the art from this image on the shirt, keep the art exactly the same" } ``` ### POST /v2/image/edit/reseason Change the season or weather of an image. **Request:** ```json { "image": "base64-or-url", "season": "winter" } ``` **Seasons:** `spring`, `summer`, `autumn`, `winter` ### POST /v2/image/edit/restyle Transform the artistic style of an image. **Request:** ```json { "image": "base64-or-url", "style": "oil_painting" } ``` **Style IDs:** `render_3d`, `cubism`, `oil_painting`, `anime`, `cartoon`, `coloring_book`, `retro_ad`, `pop_art_halftone`, `vector_art`, `story_board`, `art_nouveau`, `cross_etching`, `wood_cut` ### POST /v2/image/edit/relight Modify the lighting setup of an image. **Request:** ```json { "image": "base64-or-url", "light_type": "sunrise light", "light_direction": "front" } ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `image` | string | required | Source image URL or base64 | | `light_type` | string | "soft overcast daylight lighting" | Lighting preset (see values below) | | `light_direction` | string | required | `front`, `side`, `bottom`, `top-down` | **Light Types:** `midday`, `blue hour light`, `low-angle sunlight`, `sunrise light`, `spotlight on subject`, `overcast light`, `soft overcast daylight lighting`, `cloud-filtered lighting`, `fog-diffused lighting`, `side lighting`, `moonlight lighting`, `starlight nighttime`, `soft bokeh lighting`, `harsh studio lighting` --- ## Image Restoration & Conversion ### POST /v2/image/edit/sketch_to_colored_image Convert a sketch or line drawing to a photorealistic image. **Request:** ```json { "image": "sketch-base64-or-url" } ``` ### POST /v2/image/edit/restore Restore old/damaged photos by removing noise, scratches, and blur. **Request:** ```json { "image": "base64-or-url" } ``` ### POST /v2/image/edit/colorize Add color to B&W photos or convert to B&W. **Request:** ```json { "image": "base64-or-url", "color": "contemporary color" } ``` **Colors:** `contemporary color`, `vivid color`, `black and white colors`, `sepia vintage` ### POST /v2/image/edit/crop_foreground Remove background and crop tightly around the foreground. **Request:** ```json { "image": "base64-or-url" } ``` --- ## Structured Instructions ### POST /v2/structured_instruction/generate Generate a structured JSON instruction from natural language (no image generated). **Request:** ```json { "images": ["base64-or-url"], "instruction": "change to golden hour lighting", "mask": "optional-mask-url" } ``` **Returns:** `structured_instruction` JSON that can be passed to `/v2/image/edit` --- ## Ad Delayer - Flat Ad to Editable Layers ### POST /v2/ads/delayer Take a finished, flat ad apart into layers. Asynchronous, and a typical ad takes **2-3 minutes**. **Request:** ```json { "attachments": ["https://publicly-accessible-image-url"], "prompt": "optional guidance for the extraction", "thinking_effort": "medium", "output_format": "json", "sync": false } ``` **Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `attachments` | array | The source ad. **Exactly one** entry: a public direct image URL, raw base64, or a `data:` URI | | `prompt` | string | Optional natural-language guidance for the extraction | | `thinking_effort` | string | `low`, `medium` (default), `high` | | `output_format` | string | `json` for the layer manifest (default), `html` for the reconstructed render | | `sync` | boolean | Send `false` — the run is far longer than an HTTP response can wait | Unknown fields are rejected. Dimensions are capped at 2048 px per side unless the organisation has enterprise-tier entitlement. **The result is a pointer, not the layers.** On completion the status response carries `result.url`, which is a link to `creation.json` — a manifest of every layer with its box, paint order, text and typography. Only image layers carry an `asset_path`, which is a public URL to download separately; text layers keep their copy in the manifest, which is what makes them editable. > **`bria_call` does not complete this flow.** It looks for `result_url` / `image_url` in the > status response and gives up after 90 seconds, whereas this endpoint returns `result.url` after > 2-3 minutes, and the layers then need a second and third fetch. Use the **ad-delayer** skill, > which polls for up to 6 minutes, follows the pointer, and downloads every layer into a folder > named after the input. --- ## Status Polling ### GET /v2/status/{request_id} Check async request status. **Response:** ```json { "status": "IN_PROGRESS | COMPLETED | ERROR", "result": { "image_url": "https://..." }, "request_id": "uuid" } ``` **Status Values:** - `IN_PROGRESS` - Still processing - `COMPLETED` - Success, result available - `ERROR` - The request failed (this is the literal value; there is no `FAILED`) - `UNKNOWN` - No such request id **Polling Pattern:** ```python import requests, time def poll(status_url, api_key, timeout=120): headers = {"api_token": api_key, "User-Agent": "BriaSkills/1.4.0"} for _ in range(timeout // 2): r = requests.get(status_url, headers=headers) data = r.json() if data["status"] == "COMPLETED": return data["result"]["image_url"] if data["status"] in ("ERROR", "UNKNOWN"): raise Exception(data.get("error")) time.sleep(2) raise TimeoutError() ``` --- ## Error Handling ### HTTP Status Codes | Code | Description | |------|-------------| | 200 | Success | | 400 | Bad request | | 401 | Unauthorized - invalid API key | | 415 | Unsupported media type | | 422 | Validation failed / Content moderation blocked | | 429 | Rate limited | | 500 | Server error | ### Supported Image Formats - **Input:** JPEG, JPG, PNG, WEBP (RGB, RGBA, CMYK) - **Output:** PNG (with transparency where applicable) -
marketplace-presets.md 2.2 KB
# Marketplace Presets Specs the variant exporter enforces per channel. Values reflect each marketplace's main-image guidance at time of writing — always confirm against the latest official policy before a large publish. ## Amazon (main product image) | Rule | Value | |------|-------| | Background | Pure white `#FFFFFF` (RGB 255,255,255) | | Aspect ratio | 1:1 (square) | | Product fill | Product fills ~85% of the frame | | Minimum resolution | 1600 px longest side (≥ 1000 to zoom); 2000–3000 px recommended | | Format | JPEG (also TIFF/PNG accepted) | | Not allowed on main | Text, logos, watermarks, props, additional objects, borders | Notes: the main image must be the product only on pure white. Lifestyle/props belong in the secondary image slots, not the main. ## Shopify | Rule | Value | |------|-------| | Background | White or transparent (be consistent across the catalog) | | Aspect ratio | 1:1 primary; 4:5 also common for product pages | | Product fill | ~90%, consistent padding across all products | | Recommended resolution | 2048 × 2048 (supports zoom); max 4472 × 4472 / 20 MP | | Format | JPEG (white bg) or PNG (transparent) | Notes: Shopify is flexible, but a uniform background + padding across the catalog is what makes a store look professional. ## Etsy | Rule | Value | |------|-------| | Aspect ratio | 5:4 landscape (thumbnail crop) | | Background | White or lifestyle — Etsy encourages context/lifestyle shots | | Product fill | ~85% | | Recommended resolution | 2000 px+ on the shortest side | | Format | JPEG or PNG | Notes: Etsy thumbnails crop to ~5:4, so keep the product centered with margin so nothing important is clipped. ## How the exporter applies these `export_variants.py` (and `build_catalog.py`) for each channel: 1. Finds the product's bounding box (via alpha channel, or near-white detection). 2. Builds a canvas at the channel aspect ratio with the shortest side ≥ the channel minimum. 3. Scales the product so it spans the channel's fill ratio of the frame. 4. Centers it on the channel background (white by default; `--bg transparent` for PNG). 5. Saves JPEG (white/solid bg) or PNG (transparent). Override fill with `--fill 0.8` and background with `--bg "#F5F0E8"` when a brand needs it.
-
-
LICENSE.txt 1.3 KB
MIT License Copyright (c) 2025 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --- Note: This skill integrates with Bria.ai's API. Usage of the Bria.ai API is subject to Bria.ai's Terms of Service and API usage policies. The Bria.ai API and associated models are commercial products - please refer to https://bria.ai for licensing and pricing information. -
SKILL.md 21.5 KB
--- name: bria-ai description: Image generation, photo editing, background removal — transparent PNG images, cutouts, ecommerce packshots, product catalogs, lifestyle shots via Bria.ai. Build store-ready e-commerce catalogs at scale — shadows, product dimensions images, lifestyle scenes, marketplace listing variants. ALWAYS use this skill when the user wants to generate, edit, or transform any image — hero images, banners, social media visuals, product photos, illustrations, icons, thumbnails, ad creatives, or marketing materials — or to build a product catalog, create a packshot, stage a lifestyle shot, remove a background, make transparent PNGs, or batch-process product photos. Triggers on packshot, product catalog, product shot creator, lifestyle shot, cutout, product dimensions image, inpainting, outpainting, object removal, upscale, photo restoration, style transfer, relight, reseason, sketch-to-photo. Commercially safe, royalty-free. license: MIT metadata: author: Bria AI version: "1.4.0" --- # Bria — AI Image Generation, Editing & Background Removal Commercially safe, royalty-free image generation and editing through 20+ API endpoints. Generate from text, edit with natural language, remove backgrounds, create product shots, and build automated image pipelines. For additional endpoint details beyond what is documented here, see the [Bria API reference for agents](https://docs.bria.ai/llms.txt). ## When to Use This Skill Use this skill when the user wants to: - **Generate images** — "create an image of...", "make me a banner", "generate a hero image", "I need a product photo" - **Edit images** — "change the background", "make it look like winter", "add a vase to the table", "remove the person" - **Remove/replace backgrounds** — "make the background transparent", "cut out the product", "replace with a studio background" - **Product photography** — "create a lifestyle shot", "place this product in a kitchen scene", "e-commerce packshot" - **Enhance/transform** — "upscale this image", "make it higher resolution", "restyle as oil painting", "change the lighting" - **Batch/pipeline** — "generate 10 product images", "process all these images", "remove backgrounds in bulk" This skill handles the full spectrum of AI image operations. If the user mentions images, photos, visuals, or any visual content creation — use this skill. --- ## What You Can Build - **E-commerce product catalog** — Generate product photos, remove backgrounds for transparent PNGs, place products in lifestyle scenes (kitchen, office, outdoor), create packshots with consistent style - **Landing page visuals** — Generate hero images, abstract tech backgrounds, team photos, and section illustrations — all matching your brand aesthetic - **Social media content** — Instagram posts (1:1), Stories/Reels (9:16), LinkedIn banners (16:9), ad creatives — batch-generate variants for A/B testing - **Marketing campaign assets** — Seasonal transformations (summer→winter), restyle product shots for different markets, create localized visuals at scale - **Photo restoration pipeline** — Restore old damaged photos, colorize black & white images, upscale low-res photos to 4x, enhance quality automatically - **Brand asset toolkit** — Remove backgrounds from logos, blend artwork onto products (t-shirts, mugs), create consistent product photography across your entire catalog - **AI-powered design workflows** — Chain operations: generate→edit→remove background→place in scene→upscale — all automated through API pipelines --- ## Setup — Authentication Before making any API call, you need a valid Bria access token. ### Step 1: Check for existing credentials ```bash if [ -f ~/.bria/credentials ]; then BRIA_ACCESS_TOKEN=$(grep '^access_token=' "$HOME/.bria/credentials" | cut -d= -f2-) BRIA_API_KEY=$(grep '^api_token=' "$HOME/.bria/credentials" | cut -d= -f2-) fi if [ -z "$BRIA_ACCESS_TOKEN" ]; then echo "NO_CREDENTIALS" elif [ -n "$BRIA_API_KEY" ]; then echo "READY" else echo "CREDENTIALS_FOUND" fi ``` If the output is `READY`, skip straight to making API calls — no introspection needed. If the output is `CREDENTIALS_FOUND`, skip to Step 3. If the output is `NO_CREDENTIALS`, proceed to Step 2. ### Step 2: Authenticate via device authorization Start the device authorization flow: **2a. Request a device code:** ```bash DEVICE_RESPONSE=$(curl -s -X POST "https://engine.prod.bria-api.com/v2/auth/device/authorize" \ -H "Content-Type: application/json") echo "$DEVICE_RESPONSE" ``` Parse the response fields: - `device_code` — used to poll for the token (keep this, don't show to user) - `user_code` — the code the user must enter (e.g. `BRIA-XXXX`) - `interval` — seconds between poll attempts **2b. Show the user a single sign-in link.** Tell them exactly this — nothing more: > **Connect your Bria account:** [Click here to sign in](https://platform.bria.ai/device/verify?user_code={user_code}) > Your code is **{user_code}** — it's already filled in. Do NOT show two links. Do NOT show the raw URL separately. Do NOT use `verification_uri` from the API response. Keep it to one clickable link. **2c. Poll for the token.** After showing the user the code, immediately start polling. Try up to 60 times with the given interval (default 5 seconds): ```bash for i in $(seq 1 60); do TOKEN_RESPONSE=$(curl -s -X POST "https://engine.prod.bria-api.com/v2/auth/token" \ -d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \ -d "device_code=$DEVICE_CODE") ACCESS_TOKEN=$(printf '%s' "$TOKEN_RESPONSE" | sed -n 's/.*"access_token" *: *"\([^"]*\)".*/\1/p') if [ -n "$ACCESS_TOKEN" ]; then BRIA_ACCESS_TOKEN="$ACCESS_TOKEN" REFRESH_TOKEN=$(printf '%s' "$TOKEN_RESPONSE" | sed -n 's/.*"refresh_token" *: *"\([^"]*\)".*/\1/p') mkdir -p ~/.bria printf 'access_token=%s\nrefresh_token=%s\n' "$BRIA_ACCESS_TOKEN" "$REFRESH_TOKEN" > "$HOME/.bria/credentials" echo "AUTHENTICATED" break fi sleep 5 done ``` If the output contains `AUTHENTICATED`, proceed to Step 3. Otherwise the code expired — start over from Step 2a. **Do not proceed with any API call until authentication is confirmed.** ### Step 3: Verify billing status and resolve API key Introspect the bearer token to check billing status and obtain the real API key for Bria API calls: ```bash INTROSPECT=$(curl -s -X POST "https://engine.prod.bria-api.com/v2/auth/token/introspect" \ -d "token=$BRIA_ACCESS_TOKEN") BILLING_STATUS=$(printf '%s' "$INTROSPECT" | sed -n 's/.*"billing_status" *: *"\([^"]*\)".*/\1/p') if [ "$BILLING_STATUS" = "blocked" ]; then BILLING_MSG=$(printf '%s' "$INTROSPECT" | sed -n 's/.*"billing_message" *: *"\([^"]*\)".*/\1/p') echo "BILLING_ERROR: $BILLING_MSG" fi ACTIVE=$(printf '%s' "$INTROSPECT" | sed -n 's/.*"active" *: *\([^,}]*\).*/\1/p' | tr -d ' ') if [ "$ACTIVE" = "false" ]; then # Clear stale tokens so re-auth starts fresh (credentials file is re-created in Step 2c) printf '' > "$HOME/.bria/credentials" echo "TOKEN_EXPIRED" fi BRIA_API_KEY=$(printf '%s' "$INTROSPECT" | sed -n 's/.*"api_token" *: *"\([^"]*\)".*/\1/p') if [ -n "$BRIA_API_KEY" ]; then grep -v '^api_token=' "$HOME/.bria/credentials" > "$HOME/.bria/credentials.tmp" 2>/dev/null || true printf 'api_token=%s\n' "$BRIA_API_KEY" >> "$HOME/.bria/credentials.tmp" mv "$HOME/.bria/credentials.tmp" "$HOME/.bria/credentials" fi ``` Interpret the output: - If it prints `BILLING_ERROR: ...` — relay the message to the user exactly as shown and **stop**. Do not make any API calls. - If it prints `TOKEN_EXPIRED` — the session is no longer valid. Tell the user their session expired and restart from Step 2. - Otherwise, `BRIA_API_KEY` now contains the real API key and is cached for future calls. Proceed to the next section. --- ## Core Capabilities | Need | Capability | Use Case | |------|------------|----------| | Generate images from text | FIBO Generate | Hero images, product shots, illustrations, social media images, banners | | Edit images by text instruction | FIBO-Edit | Change colors, modify objects, transform scenes | | Combine 2–4 images in one edit | FIBO-Edit multi-reference | Put the outfit, product, logo, style, or background of one image into another | | Edit image region with mask | GenFill/Erase | Precise inpainting, add/replace specific regions | | Add/Replace/Remove objects | Text-based editing | Add vase, replace apple with pear, remove table | | Remove background (transparent PNG) | RMBG-2.0 | Extract subjects for overlays, logos, cutouts | | Turn a finished ad back into layers | Ad Delayer | make a shipped creative editable, resize or localise an ad | | Replace/blur/erase background | Background ops | Change, blur, or remove backgrounds | | Expand/outpaint images | Outpainting | Extend boundaries, change aspect ratios | | Upscale image resolution | Super Resolution | Increase resolution 2x or 4x | | Enhance image quality | Enhancement | Improve lighting, colors, details | | Restyle images | Restyle | Oil painting, anime, cartoon, 3D render | | Change lighting | Relight | Golden hour, spotlight, dramatic lighting | | Change season | Reseason | Spring, summer, autumn, winter | | Composite/blend images | Image Blending | Apply textures, logos, merge images | | Restore old photos | Restoration | Fix old/damaged photos | | Colorize images | Colorization | Add color to B&W, or convert to B&W | | Sketch to photo | Sketch2Image | Convert drawings to realistic photos | | Product cutout | Product Cutout | Clean transparent PNG from a raw product photo | | Product packshot | Product Packshot | Standardized 2000×2000 shot on a solid/clean background | | Product shadow | Product Shadow | Add a realistic drop or float shadow to a cutout | | Create product lifestyle shots | Lifestyle Shot | Place products in scenes for e-commerce | | Integrate products into scenes | Product Integrate | Embed products at exact coordinates | | Put a product in someone's hands | Product Holding | Person + product photo → person naturally holding/carrying it | | Put garments on a model | Virtual Try-On | Person + garment photo(s) → person wearing them | | Add dimension callouts to products | Product Dimensions | Marketplace-style measurement images with size/weight/capacity labels | | Build a full product catalog | Catalog Pipeline | Batch a folder of photos → packshots, dimensions, lifestyle, marketplace variants | ## How to Call Any Endpoint Use `bria_call` for all API calls. It handles URL passthrough, local file base64 encoding, JSON construction, API call, and async polling in a single function call. The API key is auto-loaded from `~/.bria/credentials`. **First**, source the helper script at `references/code-examples/bria_client.sh` (resolve relative to this skill's directory). ```bash source <SKILL_DIR>/references/code-examples/bria_client.sh # Generate (no image input — pass empty string) RESULT=$(bria_call /v2/image/generate "" '"prompt": "your description", "aspect_ratio": "16:9", "sync": true') # Remove background RESULT=$(bria_call /v2/image/edit/remove_background "/path/to/local/image.png") # Replace background RESULT=$(bria_call /v2/image/edit/replace_background "https://example.com/img.jpg" '"prompt": "sunset beach"') # Edit image (uses images array — pass --key images) RESULT=$(bria_call /v2/image/edit "/path/to/image.png" --key images '"instruction": "make it look warmer"') # Edit with reference images — each --image adds the next one, in order RESULT=$(bria_call /v2/image/edit "https://example.com/man.jpg" --key images \ --image "https://example.com/santa.png" \ '"instruction": "dress the man in image 1 in the santa outfit from image 2"') # Upscale (`desired_increase` is 2 or 4 — no other value. Transparency is preserved by default) RESULT=$(bria_call /v2/image/edit/increase_resolution "https://example.com/img.jpg" '"desired_increase": 4') # Product cutout → transparent PNG (use --key file for a local image) CUTOUT=$(bria_call /v1/product/cutout "/path/to/raw.jpg" --key file) # Packshot on white from the cutout URL RESULT=$(bria_call /v1/product/packshot "$CUTOUT" --key image_url '"background_color": "#FFFFFF"') # Lifestyle shot RESULT=$(bria_call /v1/product/lifestyle_shot_by_text "/path/to/product.png" '"scene_description": "modern kitchen countertop"') # Product holding — person + product photo, no prompt needed RESULT=$(bria_call /v2/image/edit/product/holding "/path/to/person.jpg" --key person_image \ --array-key product_images --image "https://example.com/product.png" \ '"instruction": "Replace the paper coffee cup in her right hand with the can, logo facing the camera."') # Virtual try-on — person + garment photo(s), no prompt needed. Send a full outfit together # to change several items in one call. RESULT=$(bria_call /v2/image/edit/product/virtual-tryon "/path/to/person.jpg" --key person_image \ --array-key garment_images --image "https://example.com/blazer.png" \ --image "https://example.com/trousers.png" \ '"instruction": "He wears the navy blazer over the white t-shirt he already has, and the grey tailored trousers instead of his jeans."') # Product dimensions — auto-removes background, draws measurement callouts. # Dual cm / in labels: repeat each dimension with the same name+position in both # units and set "units_display": "dual_slash" so they merge into one "12 cm / 4.7 in" label. RESULT=$(bria_call /v2/image/edit/product_dimensions "/path/to/product.png" \ '"style": "default", "units_display": "dual_slash", "dimensions": [{"name": "height", "value": 12, "unit": "cm", "position": "left"}, {"name": "height", "value": 4.7, "unit": "in", "position": "left"}, {"name": "width_bottom", "value": 6, "unit": "cm", "position": "bottom"}, {"name": "width_bottom", "value": 2.4, "unit": "in", "position": "bottom"}], "title": "Gummies Bottle", "capacity": {"value": 500, "unit": "ml"}, "weight": {"value": 250, "unit": "g", "label": "Net Weight"}') echo "$RESULT" ``` **Calling convention:** `bria_call <endpoint> <image_or_empty> [--key <json_key>] [extra JSON fields...]` - Pass a URL, local file path, or `""` (empty) for endpoints without image input - Use `--key images` when the endpoint expects an `images` array instead of `image` - Add `--image <url_or_path>` once per extra reference image (`--key images`, up to 4 in total). Order is preserved: the positional image is "image 1", the first `--image` is "image 2", … - Use `--array-key <name>` when an endpoint keys the main image and its references separately (e.g. `person_image` + `product_images`/`garment_images`): the positional image goes under `--key`, and every `--image` goes into the `--array-key` array instead - Extra JSON fields are appended as key-value pairs: `'"key": "value"'` - Returns the result image URL on success, or prints an error to stderr **Editing with several images (2–4):** reach for a second image when the look the user wants already exists as a picture — a specific outfit, product, logo, or scene — instead of something you can describe in words. Put the image being edited first, references after it, and address them by position in the instruction: *"dress the man in image 1 in the santa outfit from image 2"*. Say what each reference contributes ("the background of image 3"), in plain prose. A single-image edit needs no positional wording: *"change the mug color to red"*. **Generation options:** Aspect ratios `1:1`, `16:9`, `4:3`, `9:16`, `3:4`. Resolution `1MP` (default) or `4MP` (more detail, +30s). Pass `"sync": true` for a single generated image. `/v2/image/edit` (instruction-based FIBO-Edit) is the other way round — it always answers with a `status_url` you poll, and `"sync": true` there fails with a gateway timeout. Product Holding and Virtual Try-On accept `"sync": true` if you'd rather wait for the final image than poll — `bria_call` polls for you either way, so this only matters if you're calling the API directly. > **Advanced**: For precise control over generation, use the **vgl** skill for structured VGL JSON prompts instead of natural language. See **[API Endpoints Reference](references/api-endpoints.md)** for full parameter documentation on all 20+ endpoints. --- ## Product Catalog Pipeline (batch) When the user wants **listing-ready imagery for physical products at scale** — "build a product catalog from ./products", "turn this folder of photos into a catalog", "make these Amazon/Shopify/Etsy compliant" — use the bundled driver instead of calling endpoints one by one. It is **zero-config**: with no arguments it reads `./products` and writes `./catalog`. ```bash python3 <SKILL_DIR>/references/code-examples/build_catalog.py ``` Per product it runs **cutout → packshot + lifestyle scenes (+ dimensions) → marketplace variants** and writes `cutout.png`, `packshot.jpg`, `lifestyle_N.jpg`, `dimensions.png` (when measurements are available), per-channel variants, and a `listing.json` copy scaffold. Override only what you need: ```bash python3 <SKILL_DIR>/references/code-examples/build_catalog.py \ --input ./photos --output ./store \ --scenes "clean marble surface, soft studio light|cozy wooden desk, warm morning light" \ --variants amazon,shopify --dims ./dims.json ``` **Dimensions need real measurements — ask, don't guess.** With no measurements file and no `--no-dims`, the driver prints `NEEDS_DIMENSIONS` and exits (code 2). When that happens, stop and ask the user to either provide height/width (and optional weight/capacity) per product — as a `dims.json`/CSV — or re-run with `--no-dims` to skip only the dimensions image. Never fabricate sizes. `dims.json` format: ```json { "soap.jpg": {"title": "Hand Wash", "height_cm": 19, "width_cm": 6, "capacity_ml": 250} } ``` Store measurements in **cm** — callouts render dual **`cm / in`** automatically. Requires `pip install requests Pillow`. ### Marketplace-ready variants `export_variants.py` turns one master image (packshot or cutout) into a compliant file per channel — enforcing background, aspect ratio, product fill, and minimum resolution: ```bash python3 <SKILL_DIR>/references/code-examples/export_variants.py \ --input ./catalog/soap/packshot.jpg --output ./catalog/soap --channels amazon,shopify,etsy ``` | Channel | Aspect | Background | Product fill | Min resolution | |---------|--------|------------|--------------|----------------| | Amazon (main) | 1:1 | pure white `#FFFFFF` | ~85% | 1600 px (≥ 3000 ideal) | | Shopify | 1:1 (+ 4:5) | white / transparent | ~90% | 2048 px | | Etsy | 5:4 | white / lifestyle | ~85% | 2000 px | Full rules: **[Marketplace Presets](references/marketplace-presets.md)**. ### Write the listing copy (SEO) Bria generates images, not text — **you, the agent, write the copy**. `build_catalog.py` writes a `listing.json` scaffold per product; after the images are generated, **view each `packshot.jpg`** and fill it: `seo_title` (≤ 60 chars, keyword-first), `meta_description` (≤ 160 chars), `description` (1–2 paragraphs), `bullets` (4–6 benefit-led), `tags` (8–15 keywords). Ground every claim in what's visible — don't invent specs — and fold in any known dimensions. --- ## Prompt Engineering Tips - **Style**: "professional product photography" vs "casual snapshot", "flat design illustration" vs "3D rendered" - **Lighting**: "soft natural light", "studio lighting", "dramatic shadows" - **Background**: "white studio", "gradient", "blurred office", "transparent" - **Composition**: "centered", "rule of thirds", "negative space on left for text" - **Quality keywords**: "high quality", "professional", "commercial grade", "4K", "sharp focus" - **Negative prompts**: "blurry, low quality, pixelated", "text, watermark, logo" ### Recipes by Use Case **Hero banner (16:9):** `"Modern tech startup workspace with developers collaborating, bright natural lighting, clean minimal aesthetic"` — include "clean background" or "minimal" for text overlay space **Product photo (1:1):** `"Professional product photo of [item] on white studio background, soft shadows, commercial photography lighting"` — then remove background for transparent PNG **Presentation visual (16:9):** `"Abstract visualization of data analytics, blue and purple gradient, modern corporate style, clean composition with space for text"` — common themes: "abstract technology", "business collaboration", "minimalist geometric patterns" **Instagram post (1:1):** `"Lifestyle photo of coffee and laptop on wooden desk, morning light, cozy atmosphere"` **Story/Reel (9:16):** `"Vertical product showcase of smartphone, floating in gradient background, tech aesthetic"` --- ## Additional Resources - **[API Endpoints Reference](references/api-endpoints.md)** — Complete endpoint documentation with request/response formats for all 20+ endpoints - **[Shell Client (bria_client.sh)](references/code-examples/bria_client.sh)** — Single-function helper: `bria_call` handles auth, base64, JSON, polling - **[build_catalog.py](references/code-examples/build_catalog.py)** — Batch a folder of product photos into a store-ready catalog - **[export_variants.py](references/code-examples/export_variants.py)** — Turn a master image into Amazon/Shopify/Etsy variants - **[Marketplace Presets](references/marketplace-presets.md)** — Amazon / Shopify / Etsy image specs - **[Full API docs for agents (llms.txt)](https://docs.bria.ai/llms.txt)** — Agent-ready Bria API reference; use when this skill's summary is not enough ## Related Skills - **vgl** — Write structured VGL JSON prompts for precise, deterministic control over FIBO image generation - **ad-delayer** — Take a finished, flat ad apart into editable layers (background, hero image, logo, copy, CTA) - **image-utils** — Classic image manipulation (resize, crop, composite, watermarks) for post-processing
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.