For AI agents: this page is also available as Markdown at https://docs.refabric.com/task-api-reference/ghost_photoshoot.create.md, and the index of every page is https://docs.refabric.com/llms.txt.
Photoshoots
Create a ghost photoshoot
Shoot your products with nobody in them: on an invisible mannequin, laid flat or in close-up, one image per view. Delivered as one record whose items are the images.
Endpoint: POST https://api.refabric.com/v1/tasks/ghost_photoshoot.createTask: ghost_photoshoot.createScope: tasks:runCategory: Photoshoots
Quick start
import os
import time
import requests
headers = {"Authorization": f"Key {os.environ['REFABRIC_API_KEY']}"}
job = requests.post(
"https://api.refabric.com/v1/tasks/ghost_photoshoot.create",
headers=headers,
json={
"products": [
{
"image": "file:77",
},
],
},
).json()
print(job["job_id"])
while True:
status = requests.get(job["status_url"], headers=headers).json()
if status["lifecycle"] == "terminal":
break
time.sleep(5)
print(requests.get(job["result_url"], headers=headers).json())cURL waits for the result with Prefer: wait; when the job outlives the wait, it answers 202 with the job's URLs.
Input schema
- array<object>required
The products to shoot, one entry each, at least one. Every product gets every view:
products × viewsimages, at most 500.at least 1 items
Example:
[{"image":"file:77"},{"image":"https://files.example.com/ghost_photoshoot.create/1.jpg","photo_type":"on_model","prompt":"the striped shirt"}]- stringrequired
The product's photo, as one string (a url,
art:…orfile:…). A url or file of one of your products is that product, with its category.pattern: ^(https://|art:|file:).+$
Example:
file:77 - stringoptionalDefault:
garment_onlyWhat a ghost product's photo shows — the garment on its own, or worn by somebody.
Values
garment_only— The garment on its own — laid flat, on a hanger or a mannequin.on_model— Somebody wears the garment (say which garment inpromptwhen the photo shows several). When the product photo shows the garment worn by a person.
Example:
on_model - stringoptional
With
photo_type: on_modelonly: which garment to take from the photo, in your words ("the striped shirt"). Omitted, the main garment.Example:
the striped shirt
- stringoptionalDefault:
1:1The shape of every image.
1:1is a square frame, the usual shape for a product shown on its own in a store or catalogue grid. Send another ratio to match your own layout.Values
1:1— Square. For square places: product grids and social posts.3:4— Portrait, slightly taller than wide. For portrait product pages.4:3— Landscape, slightly wider than tall. For landscape layouts and slides.9:16— Tall portrait, as for phone screens and stories. For phone screens: stories and reels.16:9— Wide landscape, as for banners and video. For banners, headers and video frames.2:3— Portrait, as for a classic photo print. For a portrait print or a lookbook page.3:2— Landscape, as for a classic photo print. For a landscape print.4:5— Portrait, as for social feeds. For portrait posts in social feeds.
Example:
1:1 - stringoptional
The shoot's name, as it appears in your library and as the record's
data.name(at most 255 characters). Omitted: the kind of shoot and the day it was started, in UTC — "Photoshoot 2026-10-01".max length 255
- objectoptionalDefault:
{"type":"auto"}The backdrop of every image. The backdrop is chosen for you, so a shoot needs no colour from you. Send
colourwhen every image must share one exact colour, such as your store's.Example:
{"type":"colour","colour":{"hex":"#F5F2ED"}}type: auto
The backdrop is chosen for you.
- stringrequired
Picks this alternative:
auto.Values
auto
type: colour
One flat colour, locked onto every image.
- stringrequired
Picks this alternative:
colour.Values
colour
- objectrequired
One colour — as a record's
data.colours[]shows it.- stringrequired
The colour as #RRGGBB, like "#E8D9C4".
pattern: ^#[0-9A-Fa-f]{6}$
- stringoptional
A name for the colour.
- stringoptional
Its Pantone code, when known.
- array<object>optional
The views to make of every product, one
{type, view}each —ghost(on an invisible mannequin) orflat(laid flat) with its side,front,backorside, or{"type": "close_up"}for one close-up. Every listed side is made in every listed type, so list each pair (front and back, ghost and flat: four entries). Omitted, the front on an invisible mannequin.Example:
[{"type":"ghost","view":"front"},{"type":"flat","view":"front"},{"type":"ghost","view":"back"},{"type":"flat","view":"back"},{"type":"close_up"}]- stringrequired
What the view is.
Values
ghost— The product on an invisible (ghost) mannequin (view: which side). When you want the product shown on an invisible mannequin.flat— The product laid flat, as a flat-lay photo (view: which side). When you want the product laid flat.close_up— A close-up of the product's fabric and finish (one per product in a shoot). Notdetail, which is a crop of one trim or component of a look. When you want a close-up of the product's fabric and finish.
Example:
ghost - stringoptional
Which side it shows. Not sent for a
close_up.Values
front— The front of the product. For the main product image.back— The back of the product. To show what is on the back: closures, pockets, prints.side— The side of the product. To show the product's profile and fit.
Example:
front
- stringoptionalDefault:
2KThe size of every image.
2Kis sharp enough for screens and review; ask for4Kwhen the result will be printed or zoomed into.Values
2K— Standard size, for screens and review.4K— Large size, for print and zoom. When the image will be printed or zoomed into.
Example:
4K
Output schema
- array<object>optional
ONE file: the shoot,
photoshoot:<id>(application/json). Itsdata.itemsare the shoot's images —ghostorflatwith itsview(front · back · side) for a product view,close_up— each with itsurland named by its product: the product's own name, elseProduct <n>by its place inproducts, and itsexternal_id(SKU) when it has one. An image that failed carriesstatus: failedand no url. Read it again withGET /v1/files/photoshoot:<id>; pass anyitems[].urlinto an edit (image.polish, …). - objectoptional
How many images the shoot holds, how many came out with an image, and a warning for any that did not.
- integeroptional
The shoot's images.
- integeroptional
How many of them have an image.
- array<object>optional
{code, field, message}rows: one when some images were not produced (code: "output_not_produced",field: "items"; they are in the record without a url).
Required-fields example
{
"products": [
{
"image": "file:77"
}
]
}Full example
{
"products": [
{
"image": "file:77"
},
{
"image": "https://files.example.com/ghost_photoshoot.create/1.jpg",
"photo_type": "on_model",
"prompt": "the striped shirt"
}
],
"background": {
"type": "colour",
"colour": {
"hex": "#F5F2ED"
}
},
"views": [
{
"type": "ghost",
"view": "front"
},
{
"type": "flat",
"view": "front"
},
{
"type": "ghost",
"view": "back"
},
{
"type": "flat",
"view": "back"
},
{
"type": "close_up"
}
],
"aspect_ratio": "1:1",
"resolution": "4K"
}Response example
{
"job_id": "1123456789abcdef0123456789abcdef",
"lifecycle": "queued",
"status_url": "http://v3-api.refabric.com/v1/jobs/1123456789abcdef0123456789abcdef",
"result_url": "http://v3-api.refabric.com/v1/jobs/1123456789abcdef0123456789abcdef/result",
"cancel_url": "http://v3-api.refabric.com/v1/jobs/1123456789abcdef0123456789abcdef/cancel"
}Result example
{
"job_id": "1123456789abcdef0123456789abcdef",
"files": [
{
"file": "photoshoot:1123456789abcdef0123456789abcdef",
"url": "https://files.example.com/ghost_photoshoot.create/2.png",
"media_type": "application/json",
"task": "ghost_photoshoot.create",
"job_id": "1123456789abcdef0123456789abcdef",
"created_at": "2026-10-01T10:14:02Z",
"data": {
"name": "Ghost photoshoot 2026-10-01",
"status": "ready",
"items": [
{
"type": "ghost",
"view": "front",
"name": "Blazer",
"external_id": "SKU-77",
"url": "https://files.example.com/ghost_photoshoot.create/2.png"
},
{
"type": "ghost",
"view": "back",
"name": "Blazer",
"external_id": "SKU-77",
"url": "https://files.example.com/ghost_photoshoot.create/3.png"
},
{
"type": "flat",
"view": "front",
"name": "Blazer",
"external_id": "SKU-77",
"url": "https://files.example.com/ghost_photoshoot.create/4.png"
},
{
"type": "flat",
"view": "back",
"name": "Blazer",
"external_id": "SKU-77",
"url": "https://files.example.com/ghost_photoshoot.create/5.png"
},
{
"type": "close_up",
"name": "Blazer",
"external_id": "SKU-77",
"url": "https://files.example.com/ghost_photoshoot.create/6.png"
},
{
"type": "ghost",
"view": "front",
"name": "Product 2",
"url": "https://files.example.com/ghost_photoshoot.create/7.png"
},
{
"type": "ghost",
"view": "back",
"name": "Product 2",
"url": "https://files.example.com/ghost_photoshoot.create/8.png"
},
{
"type": "flat",
"view": "front",
"name": "Product 2",
"url": "https://files.example.com/ghost_photoshoot.create/9.png"
},
{
"type": "flat",
"view": "back",
"name": "Product 2",
"status": "failed"
},
{
"type": "close_up",
"name": "Product 2",
"url": "https://files.example.com/ghost_photoshoot.create/10.png"
}
]
}
}
],
"has_more": false,
"summary": {
"requested": 10,
"delivered": 9,
"warnings": [
{
"code": "output_not_produced",
"field": "items",
"message": "1 of the shoot's 10 images were not produced; they are in the record without a url."
}
]
}
}Every product gets every listed view — each side in each type, plus one close-up: 2 × (2 × 2 + 1) = 10 images; a shoot over the schema's limit is refused (too_many_outputs). prompt is taken only with photo_type: on_model; a product without photo_type is garment_only. No name was sent, so the shoot is named after its task and the UTC day. The product sent by url has no name of its own, so it is Product 2 on every one of its images.
Send product photos — the garment on its own, or worn by a person — and get catalogue images with nobody in them: on an invisible mannequin, laid flat, or in close-up. Every product gets every view you list, in one shape and on one backdrop.
Built for
- Product pages of an online store
- Ghost-mannequin and flat-lay catalogue images
- Product images taken from photos of a person wearing them
What you get
- One
photoshoot:record — the shoot's name, its status and its images indata.items. - Each image's
url, its type (ghost,flatorclose_up) and side, and the product it shows, by name and SKU. - A
summarythat counts the images asked for and made, and warns when some were not made.
Specs
| Input formats | Images, each one string: a URL, an upload (file:…) or a file from an earlier result (art:…). Upload types and sizes: GET /v1/meta limits.upload. |
|---|---|
| Input count | products: at least 1, each with its image |
| Output format | One photoshoot: record: its data is JSON in the record vocabulary, its url a preview image. |
| Output resolution | 2K, 4K |
| Aspect ratios | 1:1, 3:4, 4:3, 9:16, 16:9, 2:3, 3:2, 4:5 |
| Outputs per job | One record file, however large the shoot. Its data.items hold one image per product and view. |
Choosing a task
- Use this when you want the products alone, with nobody in the images. Use photoshoot.create when you want the garments worn by models.
Errors
field_not_acceptedfield_not_supportedinvalid_optioninvalid_requesttoo_many_outputsnot_foundrequest_refusedinsufficient_creditspermission_deniedcontent_refusedprocessing_failed
Related
image.polish· It runs after: it takes this task's result.photoshoot.create· It does a neighbouring job.mannequin_photoshoot.create· It does a neighbouring job.
Good to know
nameis at most 255 characters.productsholds at least 1 items.
Pricing: Estimate a request before you run it, or see the price of each option.
For agents and code generation
- https://api.refabric.com/v1/tasks/ghost_photoshoot.create/llms.txt
- https://api.refabric.com/v1/tasks/ghost_photoshoot.create/openapi.json
GET https://api.refabric.com/v1/tasks/ghost_photoshoot.create