{"openapi":"3.0.3","info":{"title":"Listwyse partner API","version":"2026-10-24","description":"Send vehicle photos, get finished listing photos back. Authenticate with your API key as a Bearer token. Photos and vehicles calls are the simple way in; the step-by-step calls further down still work."},"externalDocs":{"description":"The guide: how photos, vehicles, credits and webhooks work","url":"https://docs.listwyse.com"},"servers":[{"url":"https://listwyse.com/api/v1/studio"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Photos","description":"One photo in, one finished photo out. These are the calls most integrations use: send each photo as it's shot, then read it (or let the webhook tell you) when it's done."},{"name":"Vehicles","description":"A whole car in one request. Every photo in it becomes a photo like the ones above, with the same statuses, files and price, plus one vehicle.completed webhook when the last one is done."},{"name":"Dealers and settings","description":"The dealers you shoot for, each with its own settings. Settings decide how a dealer's photos come out when a call doesn't say otherwise; the id default is your account's own settings, which every dealer follows until it has its own."},{"name":"Webhooks","description":"Where we tell you about finished photos. Set a URL, try it with a test event, and see every delivery and what your server answered."},{"name":"Account and looks","description":"Your key, your credits, and the looks a photo can be put in."},{"name":"Dealership branding","description":"Set up once per dealership and used on every new vehicle. The branding kit is the logo plus a display plate, screen splash and floor mat, each an AI design or a photo of one the dealer owns. A Build-your-own look is set in the dealer's own building, made from photos of it. Provider accounts send providerDealershipId (from GET /account) on each call; other accounts leave it out."},{"name":"Step by step: vehicles","description":"A session is one vehicle. Create it once, then add photos and run tools against it. Use your stock number as externalId so repeats reuse the same session."},{"name":"Step by step: photos, sorting and logo","description":"Add listing photos, optionally a dealership logo, then sort so Studio knows which angle is which — or send each photo's angle yourself and skip the sort. Branding (wall sign, plate, screen) is optional and only needed for looks that show dealership graphics."},{"name":"VIN and shot planning","description":"A VIN names the car, unlocks film voiceovers, and lets Smart Capture pick the showroom shots that suit the car (a truck bed, a sunroof, a third row)."},{"name":"Step by step: retouch, showroom photos and film","description":"After photos are sorted (status ready), run any mix of these. Each start call returns 202 and a runId. Poll the matching GET with that runId until jobs are listed or the film has a requestId — then keep polling the session for finished URLs."},{"name":"Step by step: versions","description":"Every re-run of a shot adds a version; images[].url is the current one. Pick another version, or delete ones you don't want."},{"name":"Step by step: one job","description":"Use this when you have a requestId from jobs[] and want that one photo’s provider result. For day-to-day polling, GET /sessions/:id is enough."}],"paths":{"/photos":{"post":{"operationId":"sendPhoto","summary":"Send a photo","description":"Starts one photo and answers straight away with it queued. Send JSON with a link to the photo (url), or multipart/form-data with the photo as a file field (under 4MB) and the other fields beside it, settings and output as JSON text.\n\n**When to use it:** You have one photo and want it back finished. This is the call most integrations make, once per photo, as photos are shot.\n\n**What happens:**\n1. We check the request and hold the photo's price from your credits. The answer comes back at once: the photo, queued.\n2. We find the car by its stock number (or VIN) at that dealer, or make it if this is its first photo.\n3. In the background: we download the photo, work out what it shows, and make it.\n4. It goes through quality assurance: it's compared with your original, and made again automatically if it needs to be. This can add a few minutes.\n5. It ends completed (url is the finished file) or failed. A photo.completed or photo.failed webhook goes to your URL.\n\n**The answer:** photo is the photo as it stands (queued). vehicle is its car, with how many photos it has. credits says what was held and what's left. A photoId you've sent before answers 200 with that photo and reused: true, and holds nothing; if that photo had failed, it's tried again instead (202, its version one higher).\n\n**Errors:**\n- `DEALER_REQUIRED` (400): Your account shoots for dealers and the call didn't name one.\n- `DEALER_NOT_FOUND` (404): No dealer with that dealerId or dealerCode on your account.\n- `INVALID_REQUEST` (400): A field is missing or wrong (no stockNumber or vin, a bad VIN); message names it.\n- `INVALID_BACKGROUND` (400): background isn't \"keep\" or a look on your account.\n- `OUT_OF_CREDITS` (402): Your balance can't cover the photo. Nothing was started.\n- `PHOTO_ID_TAKEN` (409): That photoId was already sent for a different car.\n- `TOO_LARGE` (413): An uploaded file is over 4MB. Send it as a link instead.\n- `UNSUPPORTED_IMAGE` (400): An uploaded file isn't a JPEG, PNG, WebP or GIF.","tags":["Photos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"photoId":{"description":"Your own id for the photo. Sending the same photoId again returns that photo instead of making (and charging for) it twice; if it had failed, it's tried again.","type":"string","minLength":1,"maxLength":200},"url":{"type":"string","format":"uri","description":"A public https link to the photo."},"angle":{"type":"string","enum":["front","rear","side","frontAngle","rearAngle","wheel","engine","trunk","sign","door","headlight","taillight","interiorFront","interiorSide","seat","dashboard","monitor","gearStick","steeringWheel","odometer","pedals","startButton","mirror","columnControls","doorCard","seatControls","windowControls","keys","bed","sunroof","thirdRow","chargePort","hitch","topDown","frunk","runningBoards","tailgate","rearScreens","damage","exhaust","fuelDoor","spare","sideMirror","tireTread","interior","bedCover","bedTailgateOpen","details","roofAbove"],"description":"Optional. Leave it out and we work out what the photo shows. Send it only if your app already knows the shot (front, frontAngle, side, interior…); the docs list every name."},"stockNumber":{"description":"The dealer's stock number. Photos with the same stock number (and dealer) are one vehicle.","type":"string","minLength":1,"maxLength":64},"vin":{"description":"The 17-character VIN. Needed when there's no stockNumber.","type":"string"},"dealerId":{"description":"Our id for the dealer (GET /dealers). Needed if you shoot for dealers; a dealership's own account leaves it out.","type":"string"},"dealerCode":{"description":"Or your own code for the dealer, set on POST /dealers.","type":"string"},"background":{"description":"\"keep\" retouches the photo where it was taken; a look id (GET /templates) puts the car in that look. Defaults to the dealer's setting.","type":"string"},"settings":{"type":"object","properties":{"cleanup":{"type":"integer","minimum":0,"maximum":9,"description":"Clean-up: how clean the car looks (dirt, dust, water spots). Dents and scratches always stay."},"backgroundTidy":{"type":"integer","minimum":0,"maximum":9,"description":"Background tidy: how much of the lot behind the car is tidied, when the background is kept."},"reflections":{"type":"integer","minimum":0,"maximum":9,"description":"Reflections: how much of the surroundings shows in the paint and glass."},"framing":{"type":"integer","minimum":0,"maximum":9,"description":"Framing: whether a crooked photo may be straightened and re-cropped, or keeps its framing."}},"additionalProperties":false,"description":"Slider steps for these photos only, on top of the dealer's settings. Each is a step number: GET /dealers/:id/settings lists every step and its name."},"output":{"type":"object","properties":{"format":{"description":"jpeg (the default), png or webp.","type":"string","enum":["jpeg","png","webp"]}},"additionalProperties":false,"description":"The file you get back."}},"required":["url"],"additionalProperties":false},"example":{"photoId":"PMP-88231","url":"https://cdn.example.com/shoots/88231.jpg","stockNumber":"A1234","dealerCode":"RIV-014"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"The photo (under 4MB)."}},"required":["file"]}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"photo":{"id":"5b0c1e8a-7f0e-4b7a-9a43-0a1c3f2d9e11","photoId":"PMP-88231","vehicleId":"0f3e6d2c-41a7-4a0b-8f1e-3b9d2c7a6e55","stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","status":"queued","statusText":"Waiting to start","url":null,"format":"jpeg","edited":null,"angle":"frontAngle","angleSource":"given","background":"keep","version":1,"cost":0.1,"note":null,"error":null,"inputUrl":"https://cdn.example.com/shoots/88231.jpg","createdAt":"2026-10-24T15:02:11.000Z","updatedAt":"2026-10-24T15:02:11.000Z","completedAt":null},"vehicle":{"id":"0f3e6d2c-41a7-4a0b-8f1e-3b9d2c7a6e55","stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","status":"processing","photoCount":12,"counts":{"queued":0,"processing":3,"quality_assurance":1,"completed":8,"failed":0},"createdAt":"2026-10-24T15:02:11.000Z"},"credits":{"pricePerPhoto":0.1,"held":0.1,"balance":41.9}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-listwyse-paid":true},"get":{"operationId":"listPhotos","summary":"List photos","description":"Photos on your account, newest first, 50 at a time. Narrow them by vehicle, dealer or status.\n\n**When to use it:** Catching up: say, every photo still processing, or a dealer's photos from today.\n\n**The answer:** next is set when there are more: send it back as before for the page after.","tags":["Photos"],"parameters":[{"name":"vehicleId","in":"query","required":false,"description":"Only this vehicle's photos.","schema":{"type":"string"}},{"name":"dealerId","in":"query","required":false,"description":"Only this dealer's photos.","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Only photos with this status.","schema":{"type":"string","enum":["queued","processing","quality_assurance","completed","failed"]}},{"name":"limit","in":"query","required":false,"description":"How many (1–100, default 50).","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"before","in":"query","required":false,"description":"Paging: the createdAt of the last item you have; you get older ones.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"photos":[{"id":"5b0c1e8a-7f0e-4b7a-9a43-0a1c3f2d9e11","photoId":"PMP-88231","vehicleId":"0f3e6d2c-41a7-4a0b-8f1e-3b9d2c7a6e55","stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","status":"completed","statusText":"Done","url":"https://qzemyvwjmbhjilubfpzh.supabase.co/storage/v1/object/public/photos/…/p29031.jpg","format":"jpeg","edited":true,"angle":"frontAngle","angleSource":"given","background":"keep","version":1,"cost":0.1,"note":null,"error":null,"inputUrl":"https://cdn.example.com/shoots/88231.jpg","createdAt":"2026-10-24T15:02:11.000Z","updatedAt":"2026-10-24T15:05:40.000Z","completedAt":"2026-10-24T15:05:40.000Z"}],"next":"2026-10-24T15:02:11.000Z"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/photos/{id}":{"get":{"operationId":"getPhoto","summary":"Check a photo","description":"The photo as it stands now. Use our id or your own photoId in the path.\n\n**When to use it:** You want a photo's latest status, or you don't use webhooks and are waiting for one to finish.\n\n**The answer:** Once status is completed, url is the finished file. While it's still going, statusText is a sentence you can show. A failed photo carries error, with code QUALITY_ASSURANCE_FAILED when it didn't pass quality assurance.\n\n**Errors:**\n- `NOT_FOUND` (404): No photo with that id or photoId on your account.","tags":["Photos"],"parameters":[{"name":"id","in":"path","required":true,"description":"Our photo id, or your photoId.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"photo":{"id":"5b0c1e8a-7f0e-4b7a-9a43-0a1c3f2d9e11","photoId":"PMP-88231","vehicleId":"0f3e6d2c-41a7-4a0b-8f1e-3b9d2c7a6e55","stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","status":"completed","statusText":"Done","url":"https://qzemyvwjmbhjilubfpzh.supabase.co/storage/v1/object/public/photos/…/p29031.jpg","format":"jpeg","edited":true,"angle":"frontAngle","angleSource":"given","background":"keep","version":1,"cost":0.1,"note":null,"error":null,"inputUrl":"https://cdn.example.com/shoots/88231.jpg","createdAt":"2026-10-24T15:02:11.000Z","updatedAt":"2026-10-24T15:05:40.000Z","completedAt":"2026-10-24T15:05:40.000Z"}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deletePhoto","summary":"Remove a photo","description":"Takes a completed or failed photo off its car and forgets it. Its finished file keeps working at its link.\n\n**When to use it:** A car is shot again and the old photos shouldn't count towards its 60.\n\n**Errors:**\n- `PHOTO_IN_PROGRESS` (409): The photo is still being made.\n- `NOT_FOUND` (404): No photo with that id or photoId on your account.","tags":["Photos"],"parameters":[{"name":"id","in":"path","required":true,"description":"Our photo id, or your photoId.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"deleted":true,"id":"5b0c1e8a-7f0e-4b7a-9a43-0a1c3f2d9e11"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/photos/{id}/redo":{"post":{"operationId":"redoPhoto","summary":"Make a photo again","description":"Makes a completed or failed photo again as its next version. Change the background, the slider settings or the file type if you like; anything you leave out stays as it was.\n\n**When to use it:** A finished photo isn't what the dealer wanted, or a failed one is worth another try.\n\n**What happens:**\n1. The price is held again: a redo is a new photo.\n2. The photo goes back to queued with version one higher, and runs like a new photo, its checks asked afresh.\n3. The earlier version's file keeps working at its link.\n\n**Errors:**\n- `PHOTO_IN_PROGRESS` (409): The photo is still being made. Wait for it to finish.\n- `PHOTO_NOT_STORED` (409): We never received the photo (its download failed). Send it again with POST /photos and the same photoId.\n- `OUT_OF_CREDITS` (402): Your balance can't cover it.\n- `INVALID_BACKGROUND` (400): background isn't \"keep\" or a look on your account.\n- `NOT_FOUND` (404): No photo with that id or photoId on your account.","tags":["Photos"],"parameters":[{"name":"id","in":"path","required":true,"description":"Our photo id, or your photoId.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"background":{"description":"\"keep\" retouches the photo where it was taken; a look id (GET /templates) puts the car in that look. Defaults to the dealer's setting.","type":"string"},"settings":{"type":"object","properties":{"cleanup":{"type":"integer","minimum":0,"maximum":9,"description":"Clean-up: how clean the car looks (dirt, dust, water spots). Dents and scratches always stay."},"backgroundTidy":{"type":"integer","minimum":0,"maximum":9,"description":"Background tidy: how much of the lot behind the car is tidied, when the background is kept."},"reflections":{"type":"integer","minimum":0,"maximum":9,"description":"Reflections: how much of the surroundings shows in the paint and glass."},"framing":{"type":"integer","minimum":0,"maximum":9,"description":"Framing: whether a crooked photo may be straightened and re-cropped, or keeps its framing."}},"additionalProperties":false,"description":"Slider steps for these photos only, on top of the dealer's settings. Each is a step number: GET /dealers/:id/settings lists every step and its name."},"output":{"type":"object","properties":{"format":{"description":"jpeg (the default), png or webp.","type":"string","enum":["jpeg","png","webp"]}},"additionalProperties":false,"description":"The file you get back."}},"additionalProperties":false},"example":{"settings":{"cleanup":1},"output":{"format":"png"}}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"photo":{"id":"5b0c1e8a-7f0e-4b7a-9a43-0a1c3f2d9e11","photoId":"PMP-88231","vehicleId":"0f3e6d2c-41a7-4a0b-8f1e-3b9d2c7a6e55","stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","status":"queued","statusText":"Waiting to start","url":null,"format":"jpeg","edited":null,"angle":"frontAngle","angleSource":"given","background":"keep","version":2,"cost":0.1,"note":null,"error":null,"inputUrl":"https://cdn.example.com/shoots/88231.jpg","createdAt":"2026-10-24T15:02:11.000Z","updatedAt":"2026-10-24T15:02:11.000Z","completedAt":null},"credits":{"pricePerPhoto":0.1,"held":0.1,"balance":41.9}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-listwyse-paid":true}},"/vehicles":{"post":{"operationId":"sendVehicle","summary":"Send a vehicle's photos","description":"Up to 60 photos of one car in one request, as links. Each becomes a photo exactly as if it had been sent to POST /photos.\n\n**When to use it:** You collect a car's photos first and want one signal when the whole car is done.\n\n**What happens:**\n1. Every new photo's price is held together. If your balance can't cover them all, none start.\n2. The answer comes back at once: the vehicle, with this call's photos, queued.\n3. Each photo runs on its own and sends its own photo.completed or photo.failed webhook.\n4. When the last photo from this call is done, a vehicle.completed webhook carries all of them.\n\n**The answer:** batchId names this call's photos; the vehicle.completed webhook carries the same one. reused lists photos whose photoId you'd sent before: they come back as they are, uncharged (a failed one is tried again instead).\n\n**Errors:**\n- `DEALER_REQUIRED` (400): Your account shoots for dealers and the call didn't name one.\n- `DEALER_NOT_FOUND` (404): No dealer with that dealerId or dealerCode on your account.\n- `INVALID_REQUEST` (400): A field is missing or wrong; message names it.\n- `DUPLICATE_PHOTO_ID` (400): The same photoId appears twice in the call.\n- `INVALID_BACKGROUND` (400): background isn't \"keep\" or a look on your account.\n- `OUT_OF_CREDITS` (402): Your balance can't cover every new photo. Nothing was started.\n- `PHOTO_ID_TAKEN` (409): One of the photoIds was already sent for a different car.","tags":["Vehicles"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stockNumber":{"description":"The dealer's stock number. Photos with the same stock number (and dealer) are one vehicle.","type":"string","minLength":1,"maxLength":64},"vin":{"description":"The 17-character VIN. Needed when there's no stockNumber.","type":"string"},"dealerId":{"description":"Our id for the dealer (GET /dealers). Needed if you shoot for dealers; a dealership's own account leaves it out.","type":"string"},"dealerCode":{"description":"Or your own code for the dealer, set on POST /dealers.","type":"string"},"photos":{"minItems":1,"maxItems":60,"type":"array","items":{"type":"object","properties":{"photoId":{"description":"Your own id for the photo. Sending the same photoId again returns that photo instead of making (and charging for) it twice; if it had failed, it's tried again.","type":"string","minLength":1,"maxLength":200},"url":{"type":"string","format":"uri","description":"A public https link to the photo."},"angle":{"type":"string","enum":["front","rear","side","frontAngle","rearAngle","wheel","engine","trunk","sign","door","headlight","taillight","interiorFront","interiorSide","seat","dashboard","monitor","gearStick","steeringWheel","odometer","pedals","startButton","mirror","columnControls","doorCard","seatControls","windowControls","keys","bed","sunroof","thirdRow","chargePort","hitch","topDown","frunk","runningBoards","tailgate","rearScreens","damage","exhaust","fuelDoor","spare","sideMirror","tireTread","interior","bedCover","bedTailgateOpen","details","roofAbove"],"description":"Optional. Leave it out and we work out what the photo shows. Send it only if your app already knows the shot (front, frontAngle, side, interior…); the docs list every name."}},"required":["url"],"additionalProperties":false},"description":"The car's photos, up to 60."},"background":{"description":"\"keep\" retouches the photo where it was taken; a look id (GET /templates) puts the car in that look. Defaults to the dealer's setting.","type":"string"},"settings":{"type":"object","properties":{"cleanup":{"type":"integer","minimum":0,"maximum":9,"description":"Clean-up: how clean the car looks (dirt, dust, water spots). Dents and scratches always stay."},"backgroundTidy":{"type":"integer","minimum":0,"maximum":9,"description":"Background tidy: how much of the lot behind the car is tidied, when the background is kept."},"reflections":{"type":"integer","minimum":0,"maximum":9,"description":"Reflections: how much of the surroundings shows in the paint and glass."},"framing":{"type":"integer","minimum":0,"maximum":9,"description":"Framing: whether a crooked photo may be straightened and re-cropped, or keeps its framing."}},"additionalProperties":false,"description":"Slider steps for these photos only, on top of the dealer's settings. Each is a step number: GET /dealers/:id/settings lists every step and its name."},"output":{"type":"object","properties":{"format":{"description":"jpeg (the default), png or webp.","type":"string","enum":["jpeg","png","webp"]}},"additionalProperties":false,"description":"The file you get back."}},"required":["photos"],"additionalProperties":false},"example":{"stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerCode":"RIV-014","photos":[{"photoId":"PMP-88231","url":"https://cdn.example.com/shoots/88231.jpg"},{"photoId":"PMP-88232","url":"https://cdn.example.com/shoots/88232.jpg"},{"photoId":"PMP-88233","url":"https://cdn.example.com/shoots/88233.jpg"}]}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"vehicle":{"id":"0f3e6d2c-41a7-4a0b-8f1e-3b9d2c7a6e55","stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","status":"processing","photoCount":3,"counts":{"queued":0,"processing":3,"quality_assurance":1,"completed":8,"failed":0},"createdAt":"2026-10-24T15:02:11.000Z","photos":[{"id":"5b0c1e8a-7f0e-4b7a-9a43-0a1c3f2d9e11","photoId":"PMP-88231","vehicleId":"0f3e6d2c-41a7-4a0b-8f1e-3b9d2c7a6e55","stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","status":"queued","statusText":"Waiting to start","url":null,"format":"jpeg","edited":null,"angle":"frontAngle","angleSource":"given","background":"keep","version":1,"cost":0.1,"note":null,"error":null,"inputUrl":"https://cdn.example.com/shoots/88231.jpg","createdAt":"2026-10-24T15:02:11.000Z","updatedAt":"2026-10-24T15:02:11.000Z","completedAt":null},"…"]},"batchId":"7d1f2c4e-…","credits":{"pricePerPhoto":0.1,"held":0.3,"balance":41.7}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-listwyse-paid":true},"get":{"operationId":"listVehicles","summary":"List or find vehicles","description":"Cars on your account, newest first, without their photos. With stockNumber (or vin) it finds that one car; name its dealer too if you shoot for dealers.\n\n**When to use it:** Looking a car up by its stock number, or listing recent cars.\n\n**Errors:**\n- `DEALER_NOT_FOUND` (404): No dealer with that dealerId or dealerCode on your account.","tags":["Vehicles"],"parameters":[{"name":"stockNumber","in":"query","required":false,"description":"Find one vehicle by its stock number (with dealerId or dealerCode).","schema":{"type":"string"}},{"name":"vin","in":"query","required":false,"description":"Or by its VIN.","schema":{"type":"string"}},{"name":"dealerId","in":"query","required":false,"schema":{"type":"string"}},{"name":"dealerCode","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"How many (1–100, default 50).","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"before","in":"query","required":false,"description":"Paging: the createdAt of the last item you have; you get older ones.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"vehicles":[{"id":"0f3e6d2c-41a7-4a0b-8f1e-3b9d2c7a6e55","stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","status":"processing","photoCount":12,"counts":{"queued":0,"processing":3,"quality_assurance":1,"completed":8,"failed":0},"createdAt":"2026-10-24T15:02:11.000Z"}],"next":null}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/vehicles/{id}":{"get":{"operationId":"getVehicle","summary":"Check a vehicle","description":"A car and every photo sent for it, oldest first.\n\n**When to use it:** Waiting on a vehicle call without webhooks, or showing a car's progress.\n\n**The answer:** status is processing while any photo is still going, then completed (empty if it has no photos). counts says how many photos are at each status.\n\n**Errors:**\n- `NOT_FOUND` (404): No vehicle with that id on your account.","tags":["Vehicles"],"parameters":[{"name":"id","in":"path","required":true,"description":"The vehicle's id (vehicleId on any of its photos).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"vehicle":{"id":"0f3e6d2c-41a7-4a0b-8f1e-3b9d2c7a6e55","stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","status":"processing","photoCount":12,"counts":{"queued":0,"processing":3,"quality_assurance":1,"completed":8,"failed":0},"createdAt":"2026-10-24T15:02:11.000Z","photos":[{"id":"5b0c1e8a-7f0e-4b7a-9a43-0a1c3f2d9e11","photoId":"PMP-88231","vehicleId":"0f3e6d2c-41a7-4a0b-8f1e-3b9d2c7a6e55","stockNumber":"A1234","vin":"1HGCV1F34PA012345","dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","status":"completed","statusText":"Done","url":"https://qzemyvwjmbhjilubfpzh.supabase.co/storage/v1/object/public/photos/…/p29031.jpg","format":"jpeg","edited":true,"angle":"frontAngle","angleSource":"given","background":"keep","version":1,"cost":0.1,"note":null,"error":null,"inputUrl":"https://cdn.example.com/shoots/88231.jpg","createdAt":"2026-10-24T15:02:11.000Z","updatedAt":"2026-10-24T15:05:40.000Z","completedAt":"2026-10-24T15:05:40.000Z"},"…"]}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/dealers":{"get":{"operationId":"listDealers","summary":"List dealers","description":"Every dealer on your account, by name. An account that is itself one dealer lists none.","tags":["Dealers and settings"],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"dealers":[{"id":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","name":"Riverton Acura","code":"RIV-014","createdAt":"2026-10-24T14:40:00.000Z"}],"accountKind":"provider"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createDealer","summary":"Add a dealer","description":"Adds a dealer you shoot for. Give it your own code and you can name it by that code (dealerCode) on every call.\n\n**When to use it:** You start shooting for a new dealership.\n\n**The answer:** A new dealer follows your account's settings (the id default) until you change one of its own.\n\n**Errors:**\n- `ONE_DEALER_ACCOUNT` (400): Your account is a single dealer, so it has no dealers to add.\n- `DEALER_CODE_TAKEN` (409): Another of your dealers already has that code.","tags":["Dealers and settings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"The dealer's name."},"code":{"description":"Your own id for the dealer, to use as dealerCode.","type":"string","minLength":1,"maxLength":80}},"required":["name"],"additionalProperties":false},"example":{"name":"Riverton Acura","code":"RIV-014"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"example":{"dealer":{"id":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","name":"Riverton Acura","code":"RIV-014","createdAt":"2026-10-24T14:40:00.000Z"}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/dealers/{id}":{"get":{"operationId":"getDealer","summary":"Get a dealer","description":"**Errors:**\n- `DEALER_NOT_FOUND` (404): No dealer with that id or code on your account.","tags":["Dealers and settings"],"parameters":[{"name":"id","in":"path","required":true,"description":"Our dealer id, or your code.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"dealer":{"id":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","name":"Riverton Acura","code":"RIV-014","createdAt":"2026-10-24T14:40:00.000Z"}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateDealer","summary":"Rename a dealer or change its code","description":"Send only what changes. A code of null removes it.\n\n**Errors:**\n- `DEALER_CODE_TAKEN` (409): Another of your dealers already has that code.\n- `DEALER_NOT_FOUND` (404): No dealer with that id or code on your account.","tags":["Dealers and settings"],"parameters":[{"name":"id","in":"path","required":true,"description":"Our dealer id, or your code.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"code":{"description":"Your own id for the dealer; null removes it.","nullable":true,"type":"string","minLength":1,"maxLength":80}},"additionalProperties":false},"example":{"name":"Riverton Acura Sacramento"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"dealer":{"id":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","name":"Riverton Acura","code":"RIV-014","createdAt":"2026-10-24T14:40:00.000Z"}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/dealers/{id}/settings":{"get":{"operationId":"getDealerSettings","summary":"Get a dealer's settings","description":"How a dealer's photos come out when a call doesn't say otherwise, and every choice each setting has. Use the id default for your account's own settings.\n\n**The answer:** inherited is true while the dealer follows your account's settings (or Listwyse's, when your account has none of its own). options lists every background, file type and slider step you can choose, with the steps' names.\n\n**Errors:**\n- `DEALER_NOT_FOUND` (404): No dealer with that id or code on your account.","tags":["Dealers and settings"],"parameters":[{"name":"id","in":"path","required":true,"description":"Our dealer id, your code, or default for your account's own settings.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","settings":{"background":"keep","outputFormat":"jpeg","cleanup":1,"backgroundTidy":1,"reflections":1,"framing":1},"inherited":false,"options":{"background":["keep","grey-studio-catalogue","panel-showroom-catalogue","…"],"outputFormat":["jpeg","png","webp"],"sliders":{"cleanup":{"name":"Clean-up","steps":[{"step":0,"label":"As shot","description":null},{"step":1,"label":"Detailed","description":null}]},"backgroundTidy":{"name":"Background tidy","steps":["…"]},"reflections":{"name":"Reflections","steps":["…"]},"framing":{"name":"Framing","steps":["…"]}}}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateDealerSettings","summary":"Change a dealer's settings","description":"Send only the settings that change. From then on the dealer keeps settings of its own instead of following your account's.\n\n**When to use it:** Setting a new dealer up, or changing how its photos come out from now on.\n\n**The answer:** The settings as they now stand, in the same shape as GET. The same settings show in the Listwyse app.\n\n**Errors:**\n- `INVALID_REQUEST` (400): A slider step that doesn't exist, or a field we don't know.\n- `INVALID_BACKGROUND` (400): background isn't \"keep\" or a look on your account.\n- `DEALER_NOT_FOUND` (404): No dealer with that id or code on your account.","tags":["Dealers and settings"],"parameters":[{"name":"id","in":"path","required":true,"description":"Our dealer id, your code, or default for your account's own settings.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"background":{"description":"The background photos get when a call doesn't say: \"keep\" or a look id.","type":"string"},"outputFormat":{"description":"The file type photos come back as when a call doesn't say.","type":"string","enum":["jpeg","png","webp"]},"cleanup":{"type":"integer","minimum":0,"maximum":9,"description":"Clean-up: how clean the car looks (dirt, dust, water spots). Dents and scratches always stay. A step number, as listed under options."},"backgroundTidy":{"type":"integer","minimum":0,"maximum":9,"description":"Background tidy: how much of the lot behind the car is tidied, when the background is kept. A step number."},"reflections":{"type":"integer","minimum":0,"maximum":9,"description":"Reflections: how much of the surroundings shows in the paint and glass. A step number."},"framing":{"type":"integer","minimum":0,"maximum":9,"description":"Framing: whether a crooked photo may be straightened and re-cropped, or keeps its framing. A step number."}},"additionalProperties":false},"example":{"reflections":1,"outputFormat":"png"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"dealerId":"d89df6f9-0c4e-4a43-b3d4-6a2f0c9e1b77","settings":{"background":"keep","outputFormat":"jpeg","cleanup":1,"backgroundTidy":1,"reflections":1,"framing":1},"inherited":false,"options":{"background":["keep","grey-studio-catalogue","panel-showroom-catalogue","…"],"outputFormat":["jpeg","png","webp"],"sliders":{"cleanup":{"name":"Clean-up","steps":[{"step":0,"label":"As shot","description":null},{"step":1,"label":"Detailed","description":null}]},"backgroundTidy":{"name":"Background tidy","steps":["…"]},"reflections":{"name":"Reflections","steps":["…"]},"framing":{"name":"Framing","steps":["…"]}}}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook":{"get":{"operationId":"getWebhook","summary":"Your webhook","description":"The URL we send events to, the secret that signs them, and the events there are.","tags":["Webhooks"],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"webhook":{"url":"https://api.example.com/listwyse/events","secret":"9f86d081884c7d65…","events":["photo.completed","photo.failed","vehicle.completed","webhook.test"]}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"operationId":"setWebhook","summary":"Set your webhook URL","description":"An https URL on the public internet, or null to stop sending. The secret stays the same.\n\n**Errors:**\n- `INVALID_WEBHOOK_URL` (400): Not an https link on the public internet.","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"nullable":true,"description":"Your https endpoint, or null to stop webhooks.","type":"string","format":"uri"}},"required":["url"],"additionalProperties":false},"example":{"url":"https://api.example.com/listwyse/events"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"webhook":{"url":"https://api.example.com/listwyse/events","secret":"9f86d081884c7d65…"}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook/test":{"post":{"operationId":"testWebhook","summary":"Send a test event","description":"Sends a webhook.test event to your URL now, signed like the real ones.\n\n**When to use it:** Checking your endpoint receives and verifies events before any photo is sent.\n\n**The answer:** The delivery. A moment later GET /webhook/deliveries shows what your server answered.\n\n**Errors:**\n- `NO_WEBHOOK_URL` (409): Set a URL first (PUT /webhook).","tags":["Webhooks"],"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"delivery":{"id":"e2b1…","event":"webhook.test","status":"queued"}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"Recent deliveries","description":"Every event we've sent (or are about to), newest first: what your server answered, how many tries, and when the next one is.\n\n**When to use it:** Finding out why your server didn't get an event.\n\n**The answer:** status is queued, delivering, delivered or failed. A failed one with nextAttemptAt will be tried again then; one without has run out of tries (resend it).","tags":["Webhooks"],"parameters":[{"name":"limit","in":"query","required":false,"description":"How many (1–100, default 50).","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"before","in":"query","required":false,"description":"Paging: the createdAt of the last item you have; you get older ones.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"deliveries":[{"id":"e2b1…","event":"photo.completed","status":"failed","attempts":2,"responseStatus":500,"error":"Webhook endpoint returned HTTP 500.","nextAttemptAt":"2026-10-24T15:12:00.000Z","createdAt":"2026-10-24T15:05:40.000Z","deliveredAt":null,"payload":{"id":"e2b1…","event":"photo.completed","data":{"photo":{"id":"…"}}}}],"next":null}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook/deliveries/{id}/resend":{"post":{"operationId":"resendWebhookDelivery","summary":"Send a delivery again","description":"Sends one delivery again now, with the same id and body.\n\n**Errors:**\n- `NOT_FOUND` (404): No delivery with that id for your key.\n- `DELIVERING` (409): It's being sent right now.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"The delivery's id.","schema":{"type":"string"}}],"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"delivery":{"id":"e2b1…","status":"queued"}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/account":{"get":{"operationId":"getAccount","summary":"Check your key and credits","description":"Who the key belongs to, its credit balance and price per photo, and the limits.\n\n**When to use it:** Your first call, to check the key works; and before a big send, to check the balance.\n\n**The answer:** credits.balance is what's left to spend; credits.pricePerPhoto is what one finished photo costs. organization.kind is provider for an account that shoots for dealers (dealerships lists them) and dealership for one dealer's own account.","tags":["Account and looks"],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"key":{"id":"…","name":"Pro-MotionPix","prefix":"sk_live_1d8d34c6…","webhookUrl":"https://api.example.com/listwyse/events"},"organization":{"id":"…","name":"Pro-MotionPix","kind":"provider"},"dealerships":[{"id":"d89df6f9-…","name":"Riverton Acura"}],"credits":{"balance":41.9,"pricePerPhoto":0.1},"limits":{"photosPerVehicle":60,"photosPerVehicleCall":60,"uploadBytes":4194304}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates":{"get":{"operationId":"listLooks","summary":"List the looks","description":"Every look a photo can be put in: Listwyse's own, and any your account has built. Use a look's id as background.\n\n**The answer:** Skip looks with videoOnly: true; they only make films.","tags":["Account and looks"],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"templates":[{"id":"panel-showroom-catalogue","name":"Lumen","description":"Gloss floor, light-panel ceiling.","builtIn":true,"videoOnly":false},{"id":"custom:3b5f…","name":"Riverton showroom","builtIn":false,"videoOnly":false}]}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/branding":{"get":{"operationId":"getBranding","summary":"The branding kit: the logo and, for each asset, its saved designs, which one is chosen, and whether one is being made.","description":"Assets are displayPlate, monitor (the screen) and floorMat. New vehicles get the chosen designs, and one plate design serves the front and rear plate shots. Vehicles made earlier keep what they had.","tags":["Dealership branding"],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"branding":{"dealership":{"name":"Riverton Acura","providerDealershipId":null},"logoUrl":"https://…","assets":{"displayPlate":{"chosenUrl":"https://…","designs":[{"url":"https://…","uploaded":false}],"generating":false,"runId":null,"error":null},"monitor":{"chosenUrl":null,"designs":[],"generating":true,"runId":"run_…","error":null},"floorMat":{"chosenUrl":"https://…","designs":[{"url":"https://…","uploaded":true}],"generating":false,"runId":null,"error":null}}}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchBranding","summary":"Switch which saved design new vehicles get.","tags":["Dealership branding"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"chosen":{"floorMat":"https://…"}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"branding\": { … } }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/branding/logo":{"post":{"operationId":"postBrandingLogo","summary":"Set the dealership logo: JSON { url } for a logo you host, or a multipart file field.","description":"Transparent areas are flattened onto white. Designs made from an earlier logo stay in use until you make new ones.","tags":["Dealership branding"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"url":"https://cdn.example/logo.png"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"branding\": { \"logoUrl\": \"https://…\", \"assets\": { … } } }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/branding/designs":{"post":{"operationId":"postBrandingDesigns","summary":"Paid AI work: design a plate, screen and/or mat from the logo. One design each, chosen as soon as it lands.","description":"kinds defaults to all three. Takes a minute or two: poll GET /branding until the asset's generating is false. 409 if every asked asset is already being designed; 400 without a logo.","tags":["Dealership branding"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"kinds":["displayPlate","monitor","floorMat"]}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"branding":{"assets":{"displayPlate":{"generating":true,"runId":"run_…"}}}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-listwyse-paid":true},"delete":{"operationId":"deleteBrandingDesigns","summary":"Delete one saved design. If it was chosen, the next saved one is.","tags":["Dealership branding"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"kind":"displayPlate","url":"https://…"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"branding\": { … } }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/branding/photos":{"post":{"operationId":"postBrandingPhotos","summary":"Use the dealer's own photo of an asset, say the floor mat they really own. It is chosen at once.","description":"JSON { kind, url }, or multipart kind + file.","tags":["Dealership branding"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"kind":"floorMat","url":"https://cdn.example/our-mat.jpg"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"kind\": \"floorMat\", \"url\": \"https://…\", \"branding\": { … } }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates/custom":{"post":{"operationId":"postTemplatesCustom","summary":"Start a Build-your-own look from photos of the dealership's building (up to 8).","description":"Storefront, showroom floor, signage. note steers the build. Once built and picked, its templateId works anywhere a look does.","tags":["Dealership branding"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"name":"Our showroom","note":"Show the red wall logo behind the car","photoUrls":["https://…/front.jpg","https://…/floor.jpg"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"example":{"template":{"id":"d2343166-…","templateId":"custom:d2343166-…","name":"Our showroom","status":"draft","photoUrls":["https://…"],"exampleUrls":[],"selectedUrl":null}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"getTemplatesCustom","summary":"Every Build-your-own look on the account, in any status.","description":"status is draft (add photos, then build), generating, examples (pick one), ready (usable) or error. GET /templates lists only ready ones. Add ?providerDealershipId= for one dealership's.","tags":["Dealership branding"],"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"templates\": [{ \"id\": \"…\", \"templateId\": \"custom:…\", \"status\": \"ready\", … }] }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates/custom/{id}":{"get":{"operationId":"getTemplatesCustomId","summary":"One Build-your-own look. :id is its id or its templateId.","tags":["Dealership branding"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"template\": { \"status\": \"examples\", \"exampleUrls\": [\"https://…\"], … } }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchTemplatesCustomId","summary":"Pick the built photo (selectedUrl, one of exampleUrls) to make the look ready, or rename it / change its note.","tags":["Dealership branding"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"selectedUrl":"https://…","name":"Our showroom"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"template\": { \"status\": \"ready\", \"selectedUrl\": \"https://…\", … } }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteTemplatesCustomId","summary":"Delete a Build-your-own look. Vehicles already made with it keep their photos.","tags":["Dealership branding"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"deleted":true,"id":"…"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates/custom/{id}/photos":{"post":{"operationId":"postTemplatesCustomIdPhotos","summary":"Add more photos of the building: JSON { urls }, or a multipart file.","tags":["Dealership branding"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"urls":["https://…/entrance.jpg"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"template\": { \"photoUrls\": [\"https://…\", \"https://…\"], … } }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteTemplatesCustomIdPhotos","summary":"Take a photo off. The look goes back to draft until built again.","tags":["Dealership branding"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"url":"https://…"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"template\": { \"status\": \"draft\", … } }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates/custom/{id}/build":{"post":{"operationId":"postTemplatesCustomIdBuild","summary":"Paid AI work: turn the photos into an empty, tidied-up photo of the place with room for a car.","description":"Takes a few minutes. Poll GET /templates/custom/:id until status is examples, then PATCH it with selectedUrl. A running build is returned, not restarted. A ready look needs regenerate: true, and is unusable until a new photo is picked.","tags":["Dealership branding"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"regenerate":false}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":"{\n  \"template\": { \"status\": \"generating\", … },\n  \"runId\": \"run_…\",\n  \"operationId\": \"…\",\n  \"started\": true\n}"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-listwyse-paid":true}},"/templates/custom/{id}/plate":{"post":{"operationId":"postTemplatesCustomIdPlate","summary":"Skip the build: use a finished, empty photo of the dealership. The look is ready at once.","description":"JSON { url, name? }, or multipart file + name.","tags":["Dealership branding"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"url":"https://…/empty-showroom.jpg","name":"Our showroom"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"template\": { \"status\": \"ready\", … } }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions":{"post":{"operationId":"postSessions","summary":"Start a session for a vehicle. If this key already has that externalId, you get the existing session back (reused: true).","description":"label, externalId, dealershipId, vin and providerDealershipId are all optional. VIN must be 17 characters if you send one. providerDealershipId is a Listwyse dealership on your account (GET /account). The vehicle gets the dealership's branding kit (GET /branding).","tags":["Step by step: vehicles"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"label":"2024 Lexus GX 550","externalId":"stock-4412","dealershipId":"rooftop-14","vin":"JTJTBKDJ4R5012345"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"session":{"id":"…","status":"needs_photos","label":"2024 Lexus GX 550","externalId":"stock-4412","vin":"JTJTBKDJ4R5012345"},"reused":false}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"getSessions","summary":"List recent sessions for this key, newest first.","description":"?externalId=stock-4412 finds one car by your stock number. ?limit= takes 1–100 (default 60). This list is a summary without a status field — use generating and progress to see if work is still running, then GET a single session for full results.","tags":["Step by step: vehicles"],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"sessions":[{"id":"…","label":"2024 Lexus GX 550","externalId":"stock-4412","uploadCount":12,"imageCount":4,"enhancedCount":0,"showroomImageCount":4,"videoCount":0,"generating":true,"thumbnailUrl":"https://…"}]}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}":{"get":{"operationId":"getSessionsId","summary":"The one call to poll. Returns the vehicle, original photos, sorted angles, finished photos, films, and anything still in progress.","description":"When status is complete, take URLs from images[].url and videos[].url. Each images[] item is pending, completed, or error. urls lists every version of a shot (re-runs add versions) and url is the current one — the last of urls. progress says which tools are still running.","tags":["Step by step: vehicles"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"session":{"id":"…","status":"generating","label":"2024 Lexus GX 550","externalId":"stock-4412","vin":"JTJTBKDJ4R5012345","slots":{"front":["https://…"]},"images":[{"presetId":201,"title":"Hero","status":"completed","url":"https://…/v2.jpg","urls":["https://…/v1.jpg","https://…/v2.jpg"]}],"videos":[{"requestId":"…","status":"pending"}],"pendingImageJobs":[{"requestId":"…","presetId":201,"title":"Hero"}],"progress":{"sorting":false,"enhance":false,"images":true,"video":false,"importing":false},"operations":[{"id":"…","kind":"images","status":"running"}],"errors":[]}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchSessionsId","summary":"Update the display name and/or VIN.","tags":["Step by step: vehicles"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"label":"GX 550 Overtrail","vin":"JTJTBKDJ4R5012345"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true,"label":"GX 550 Overtrail","vin":"JTJTBKDJ4R5012345"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteSessionsId","summary":"Delete the session and its results from your account.","description":"Photo and result URLs you already copied keep working.","tags":["Step by step: vehicles"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/photos":{"post":{"operationId":"postSessionsIdPhotos","summary":"Add listing photos from https URLs you already host.","description":"Send { urls } when you don't know the angles, or { photos: [{ url, slot }] } when you do. A photo with a slot goes straight onto that angle (replacing the one there; sign, damage and engine photos add up instead, up to 5, and interior photos up to 20, and each one becomes its own image) and needs no sort; a photo without one needs a sort, and a sort re-labels every photo. The sort asks first whether each photo was taken inside the car: every cabin photo goes under interior (one image each) with three labels (mileage or key info, sunroof, damage) that change how it is made; any other photo gets its angle. Slots: front, frontAngle, side, rear, rearAngle, wheel, headlight, taillight, interior, interiorFront, interiorSide, dashboard, steeringWheel, odometer, trunk, engine and more (an unknown slot returns the full list). You can also send one image as multipart form-data with a file field and an optional slot field. A session holds at most 48 photos; each uploaded file must be under 4MB.","tags":["Step by step: photos, sorting and logo"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"photos":[{"url":"https://cdn.example/front.jpg","slot":"front"},{"url":"https://cdn.example/side.jpg","slot":"side"}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"uploads":[{"url":"https://…","name":"front.jpg"}],"slots":{"front":["https://…"],"side":["https://…"]},"skipped":0}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteSessionsIdPhotos","summary":"Take one photo off the vehicle, by its uploads[].url.","description":"The angles that photo filled lose it; the other angles stay sorted, so no new sort is needed.","tags":["Step by step: photos, sorting and logo"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"url":"https://…/inputs/….jpg"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true,"uploads":[{"url":"https://…"}],"slots":{"front":["https://…"]}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/import":{"post":{"operationId":"postSessionsIdImport","summary":"Pull photos from a public listing page instead of sending URLs yourself. Answers at once (202) and imports in the background, so you can start many vehicles in parallel.","description":"The vehicle's status is importing until it finishes (usually 1–3 minutes); poll GET /sessions/:id, then sort. A failed import shows in errors[] (e.g. a site that blocks automated imports — add photos with /photos instead). A VIN on the page is stored when we can read one. A second import on the same vehicle while one is running returns 409.","tags":["Step by step: photos, sorting and logo"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"listingUrl":"https://dealer.example/inventory/4412"}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"pending":true,"runId":"run_…","listingUrl":"https://dealer.example/inventory/4412","operationId":"…","pollIntervalMs":5000}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"getSessionsIdImportPoll","summary":"Check one import: still running, the photos it added, or why it failed.","description":"Add ?runId=run_….","tags":["Step by step: photos, sorting and logo"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"vehicleName":"2024 Lexus GX 550","label":"2024 Lexus GX 550","vin":"JTJTBKDJ4R5012345","uploads":[{"url":"https://…","name":"listing-1.jpg"}],"skipped":0,"runId":"run_…"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/logo":{"post":{"operationId":"postSessionsIdLogo","summary":"Set the dealership logo: a multipart file field, or JSON { url } for a logo you host.","description":"Required before generating a look that shows branding. Send kind wallMount if you already have a pre-rendered wall sign image. Uploaded files must be under 4MB; logos by URL can be up to 12MB.","tags":["Step by step: photos, sorting and logo"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"url":"https://cdn.example/logo.png","kind":"logo"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"kind":"logo","upload":{"url":"https://…"},"logoUrl":"https://…"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/sort":{"post":{"operationId":"postSessionsIdSort","summary":"Group photos by camera angle (front, side, interior, and so on). Required before showroom photos or a film.","description":"Returns 202 while sorting. Poll GET /sessions/:id until status is ready, or GET /sessions/:id/sort. Pass force: true to sort again after you add photos. removeInfographics: true drops price banners burned into the image.","tags":["Step by step: photos, sorting and logo"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"force":false,"removeInfographics":false}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"pending":true,"runId":"run_…","operationId":"…"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"getSessionsIdSort","summary":"Check whether sorting has finished.","description":"Optional ?runId= from the POST. If sorting is already done, this returns the grouped angles (cached: true) instead of pending.","tags":["Step by step: photos, sorting and logo"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"pending":true,"runId":"run_…"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/branding":{"post":{"operationId":"postSessionsIdBranding","summary":"Build wall-sign, display-plate, and screen graphics from the logo.","description":"You can skip this call — generating showroom photos will build branding first when the look needs it. Returns 409 if photos are still sorting. regenerate: true rebuilds even if assets already exist.","tags":["Step by step: photos, sorting and logo"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"toggles":{"wallSign":true,"displayPlate":true,"monitor":false},"templateId":"showroom-set-v2"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"branding":{"wallSign":true,"displayPlate":true,"monitor":false},"brandingAssets":{"wallSign":"https://…"},"built":["wallSign"]}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/vin":{"post":{"operationId":"postSessionsIdVin","summary":"Set the VIN: typed, or read from a photo of the door-frame sticker or windshield plate.","description":"Send { vin }, { photoUrl }, or a multipart file. The photo is not added to the listing photos. An unnamed vehicle is named from the VIN. vin: null means the photo couldn't be read and nothing was saved. checkDigit: false skips the North American check digit for other markets.","tags":["VIN and shot planning"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"photoUrl":"https://cdn.example/door-sticker.jpg"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"vin":"JTJTBKDJ4R5012345","label":"2024 Lexus GX 550 Overtrail","decoded":{"year":2024,"make":"Lexus","model":"GX 550","trim":"Overtrail","fuel":"Gasoline"}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/vin/{vin}":{"get":{"operationId":"getVinVin","summary":"Window-sticker facts for any VIN: specs, standard equipment, EPA fuel economy and NHTSA crash ratings.","description":"From free government sources. Equipment lists only what the decode marks as standard.","tags":["VIN and shot planning"],"parameters":[{"name":"vin","in":"path","required":true,"description":"vin","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"facts":{"vin":"JTJTBKDJ4R5012345","year":2024,"make":"Lexus","model":"GX 550","engine":"3.4L 6-cylinder turbo, 349 hp","drivetrain":"4WD","equipment":[{"title":"Safety","items":["…"]}],"fuelEconomy":{"unit":"MPG","combined":17,"city":15,"highway":21},"safety":{"overall":5}}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/shot-plan":{"post":{"operationId":"postSessionsIdShotplan","summary":"Smart Capture: the showroom shots picked for this car from its VIN, and the photo angle each one needs.","description":"Showroom photo runs use this list when shotMode is smart (the default). A shot whose needsPhoto angle has no photo is swapped for the next best. Built on the first call and kept; refresh: true rebuilds it. plan is null until the car has a VIN.","tags":["VIN and shot planning"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"refresh":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"plan":{"vin":"…","name":"2024 Ram 1500 Limited","shots":[{"key":"01","title":"Worm's-eye front 3/4","reason":null,"needsPhoto":"frontAngle"},{"key":"51","title":"Truck bed","reason":"Pickup: the bed","needsPhoto":"bed"}],"highlights":["Truck bed"]},"shotMode":"smart"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shot-plan":{"get":{"operationId":"getShotplanPoll","summary":"The same plan for a VIN before the car is photographed, so you know which photos to take. Nothing is saved.","description":"Add ?vin=….","tags":["VIN and shot planning"],"responses":{"200":{"description":"OK","content":{"application/json":{"example":"{ \"plan\": { \"vin\": \"…\", \"shots\": [ … ], \"highlights\": [ … ] } }"}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/check-shot":{"post":{"operationId":"postSessionsIdCheckshot","summary":"Check that a photo shows the angle it was taken for, or ask which angle it shows.","description":"url must be one of the vehicle's photos. Leave slot out to only ask what it shows. verdict is match, mismatch or unsure. Saved on the vehicle under shotChecks; asking again returns the saved check.","tags":["VIN and shot planning"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"url":"https://…/inputs/….jpg","slot":"frontAngle"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"check":{"expected":"frontAngle","verdict":"mismatch","seen":"side","note":"Side profile, not a three-quarter view."}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/enhance":{"post":{"operationId":"postSessionsIdEnhance","summary":"No background: retouch each original photo once (lighting, reflections, clutter), keeping its framing.","description":"Does not require a look. Photos must already be on the session; ones not yet sorted are sorted first. Each result keeps its photo's shape. sourceUrls (from uploads[].url) redoes just those photos. configId (the retired Enhance Configs) is ignored.","tags":["Step by step: retouch, showroom photos and film"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"sourceUrls":["https://…/inputs/….jpg"]}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"preparing":true,"runId":"run_…","operationId":"…","jobs":[],"pollIntervalMs":2500}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"getSessionsIdEnhancePoll","summary":"Wait until each enhanced photo has been queued.","description":"runId is required. When completed is true, jobs[] has a requestId per photo. Finished files then appear on GET /sessions/:id under images. Add ?runId=run_….","tags":["Step by step: retouch, showroom photos and film"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"completed":true,"jobs":[{"presetId":29001,"title":"Enhance — Front","requestId":"…"}]}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/images":{"post":{"operationId":"postSessionsIdImages","summary":"Generate showroom photos for a look. Branding for that look is built first if needed.","description":"Photos must be sorted. Looks that are video-only return 400 — use /video instead. Optional presetIds: [201, 203] makes only those shots; to re-make shots you already have, send their images[].presetId and templateId can be left out. Each re-run adds a version. With presetIds, updateTwins: true also puts each new photo on the other cars of the dealership sharing that photo (same photo files, look and settings).","tags":["Step by step: retouch, showroom photos and film"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"templateId":"panel-showroom-catalogue"}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"preparing":true,"runId":"run_…","templateId":"panel-showroom-catalogue","jobs":[],"pollIntervalMs":2500}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"getSessionsIdImagesPoll","summary":"Wait until showroom shots are queued.","description":"runId is required. When jobs[] is filled, poll the session until those photos show status completed. Add ?runId=run_….","tags":["Step by step: retouch, showroom photos and film"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"templateId":"panel-showroom-catalogue","jobs":[{"presetId":201,"title":"Hero","requestId":"…"}],"brandingAssets":{"wallSign":"https://…"}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/video":{"post":{"operationId":"postSessionsIdVideo","summary":"Start the 16:9 film for a look (15 seconds; 10 for a custom template). Missing showroom photos for that look are generated first.","description":"templateId is required. imageTemplateId is accepted as the same field. Photos must be sorted. Looks without a film recipe return 400. templateId \"enhance\" makes the film from the car's enhanced photos (run POST /enhance first; 409 until there are some).","tags":["Step by step: retouch, showroom photos and film"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"templateId":"panel-showroom-catalogue"}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"preparing":true,"runId":"run_…","templateId":"panel-showroom-catalogue","pollIntervalMs":2500}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"getSessionsIdVideoPoll","summary":"Wait until the film has been submitted, then until a url is present.","description":"runId is required. queued + requestId means the film is rendering. completed + url means you can download it. The session videos[] list updates at the same time. Add ?runId=run_….","tags":["Step by step: retouch, showroom photos and film"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"queued":true,"completed":false,"requestId":"…","templateId":"panel-showroom-catalogue"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/voiceover":{"post":{"operationId":"postSessionsIdVoiceover","summary":"Voice a finished film: a script written from the car's VIN, read over the film.","description":"requestId is the film's videos[].requestId. The car needs a VIN (send vin here to set one). Returns 202 with a runId.","tags":["Step by step: retouch, showroom photos and film"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"requestId":"…","vin":"JTJTBKDJ4R5012345"}}}},"responses":{"202":{"description":"Accepted: the work runs in the background","content":{"application/json":{"example":{"pending":true,"runId":"run_…","pollIntervalMs":2500}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"getSessionsIdVoiceoverPoll","summary":"Wait for the voiced film.","description":"completed + url is the film with the voiceover. It also appears on the session as videos[].voiceover. Add ?runId=run_….","tags":["Step by step: retouch, showroom photos and film"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"completed":true,"url":"https://…/voiced.mp4","script":"…"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/versions":{"post":{"operationId":"postSessionsIdVersions","summary":"Make one of a shot's versions (images[].urls) its current photo.","tags":["Step by step: versions"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"presetId":201,"url":"https://…/v1.jpg"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true,"presetId":201,"url":"https://…/v1.jpg"}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteSessionsIdVersions","summary":"Delete generated photos. A shot with no versions left drops out of images.","tags":["Step by step: versions"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"},"example":{"images":[{"presetId":201,"url":"https://…/v1.jpg"}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"ok":true,"removed":1}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{id}/jobs/{requestId}":{"get":{"operationId":"getSessionsIdJobsRequestId","summary":"Inspect one photo job, including the finished file or the error.","description":"Job status uses succeeded / failed (not completed / error like session.images[]).","tags":["Step by step: one job"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id from the call that made it.","schema":{"type":"string"}},{"name":"requestId","in":"path","required":true,"description":"requestId","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"job":{"requestId":"…","operationId":"…","kind":"image","presetId":201,"title":"Hero","status":"succeeded","result":{"images":[{"url":"https://…"}]}}}}}},"default":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Your API key (sk_live_…)."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"OUT_OF_CREDITS"},"message":{"type":"string","example":"Not enough credits: this needs 0.1 and the account has 0."}},"required":["code","message"]},"requestId":{"type":"string","description":"Quote it when you ask us about a call."}},"required":["error","requestId"]}}}}