---
title: "Generate a marketplace-ready listing"
method: POST
path: "/v1/listings/generate"
tags: ["Listings"]
---

# Generate a marketplace-ready listing

`POST /v1/listings/generate`

## Request

- Base URL: `https://api.shopfront.app`
- URL: `https://api.shopfront.app/v1/listings/generate`
- Auth: HTTP bearer

Generate a complete listing from product images using AI. Returns core listing data,
marketplace mappings (eBay, Depop, Grailed, Shopify), pricing recommendations, and image URLs.

**Modes:**
- **sync** (default for API tokens): Returns the generated listing in the HTTP response.
  No enrichment — fast path for API consumers.
- **async** (default for JWT/frontend): Returns `{ jobId, displayId }` immediately.
  Listing is persisted and enriched in the background.

The mode is auto-detected from your auth type, or you can set it explicitly via `mode`.

**Idempotency (sync mode):**
Pass an `Idempotency-Key` header to safely retry requests. If a response was already
generated for this key within the last 24 hours, the cached response is returned.

**Usage limits:**
This endpoint checks your organisation's monthly allowance and billing status before generation.
Returns 402 if your monthly limit has been reached or billing is blocked; read the returned error.

## Headers

- `Idempotency-Key` string

## Request body

- ListingGenerateRequest: Request body for listing generation. Provide images as URLs (`imageUrls`) or base64 strings (`images`). The first image is always used as the hero.
  - `requestId` string, uuid, required: Client-generated UUID for request tracking
  - `imageUrls` string[]: Image URLs to download. The first image is used as the hero. Supports JPEG, PNG, WebP, AVIF, GIF, TIFF, HEIC and HEIF; other formats are refused with 400 `UNSUPPORTED_IMAGE_FORMAT`. Non-JPEG images are converted to JPEG.
  - `images` string[]: Images as base64-encoded strings. The first image is used as the hero. Each string should be raw base64 data (no `data:image/...;base64,` prefix needed). Use this if you already have image data in memory. For most cases, `imageUrls` is simpler. Supports JPEG, PNG, WebP, AVIF, GIF, TIFF, HEIC and HEIF; other formats are refused with 400 `UNSUPPORTED_IMAGE_FORMAT`. Non-JPEG images are converted to JPEG.
  - `channels` string[]: Marketplace channels to generate mappings for. Empty = core listing only.
  - `mode` 'sync' | 'async': Response mode. Defaults to `sync` for API token callers, `async` for JWT/frontend callers. - **sync**: Returns full listing in the HTTP response body (no enrichment) - **async**: Returns `{ jobId, displayId }`, runs persist + enrichment in background
  - `persist` boolean: Whether to save the listing to the database. Defaults to `false` in sync mode, `true` in async mode.
  - `applyChannelTemplates` boolean: Apply per-channel templates to generate marketplace-specific titles and descriptions. Uses the templates configured in Settings → Marketplaces → Templates for each channel. Set to `false` to skip and use the core listing content for all channels.
  - `workflow` union: Controls which workflow the listing is saved to (only applies when `persist: true`). - `"auto"` (default): adds to a rolling daily "API Listings" workflow, creating one if needed - `"new"`: creates a fresh workflow for this listing - `1162` (number): adds to workflow #1162 (the number from the dashboard URL)
    - 'auto' | 'new'
    - integer
  - `imageS3Keys` object: S3 keys from presigned upload (alternative to `imageUrls` / `images`). Get keys from `POST /v1/listings/presign`.
    - `hero` string: S3 key for the hero image
    - `images` string[]: S3 keys for all images in order
  - `templateId` string, uuid, nullable: Base template ID. Null explicitly selects No template. When omitted, the visible organisation general default applies, if one exists.
  - `enableBackgroundRemoval` boolean: Remove the background from the hero image and replace with a clean white backdrop. Processing duration varies.
  - `enableImageEnhancement` boolean: Apply AI image enhancement to the hero image (lighting adjustments, preservation of product colours and details). Processing duration varies.
  - `aiNotes` string: Additional context for the AI (e.g., "Small scuff on left toe, purchased 2023")
  - `ebayMarketplace` 'EBAY_US' | 'EBAY_AU' | 'EBAY_GB' | 'EBAY_DE' | 'EBAY_CA' | 'EBAY_FR': eBay marketplace for category mapping
  - `targetCurrency` string: Currency for pricing results (ISO 4217 three-letter code)

## Response `200`

Sync mode — full listing returned

- object
  - `data` PublicListingResponse: Complete listing result returned in sync mode
    - `listing` object: Core listing data
      - `title` string
      - `description` string
      - `brand` string, nullable
      - `condition` 'New' | 'Like New' | 'Good' | 'Fair' | 'Poor'
      - `conditionNotes` string, nullable: Customer-facing condition description, independent of requested marketplaces. Null when absent or cleared; distinct from the condition grade and general description.
      - `sizeSystem` 'US' | 'UK' | 'EU' | 'FR' | 'AU' | 'IT', nullable: Source sizing system, when known.
      - `size` string, nullable
      - `colour` string, nullable
      - `material` string, nullable
      - `pattern` string, nullable
      - `gender` string, nullable
      - `measurements` object, nullable
      - `tags` string[]
      - `productType` string
      - `category` string, nullable
      - `packageWeight` number, nullable: AI-estimated package weight in kilograms (item plus typical packaging), for shipping calculations. Null when the generator could not produce an estimate.
      - `packageWeightConfidence` number, nullable: Confidence of the AI weight estimate, for estimate triage. The model states what its estimate rests on; that basis maps to calibrated tiers in code (0.85 standard catalogue item, 0.7 adjusted from size/material/type, 0.4 rough guess). Null when packageWeight is null or the model did not state an estimation basis.
      - `packageDimensions` object, nullable: AI-estimated packaged parcel dimensions in centimetres (the parcel once folded/boxed for shipping; height is the thickness). Null when the generator could not produce an estimate.
        - `height` number
        - `width` number
        - `length` number
      - `packageDimensionsConfidence` number, nullable: As packageWeightConfidence, for the dimensions estimate — one shared estimation basis drives both. Null when packageDimensions is null or no basis was stated.
      - `identificationConfidence` number: Confidence of the product identification (0-1) — how certain the pipeline is about what the item is, which is what the generated title and description are built from. This is the score to surface next to the title. Marketplace-level confidence fields describe narrower decisions (for example a category match) and must not be read as title confidence.
    - `marketplaces` object: Marketplace-specific mappings (only channels requested)
      - `ebay` object, nullable
        - `categoryId` string
        - `categoryName` string
        - `conditionId` string
        - `itemSpecifics` object
      - `depop` object, nullable
        - `categoryId` integer, nullable
        - `brandId` integer, nullable
        - `condition` string
        - `colour` string, nullable
        - `size` string, nullable
      - `grailed` object, nullable
        - `designerId` string, nullable
        - `department` string, nullable
        - `category` string, nullable
        - `subcategory` string, nullable
        - `condition` string
        - `size` string, nullable
      - `shopify` object, nullable
        - `title` string
        - `body_html` string
        - `vendor` string, nullable
        - `product_type` string
    - `pricing` object, nullable: Pricing recommendations (null if insufficient market data)
      - `recommended` number
      - `quickSale` number
      - `premium` number
      - `marketAverage` number
      - `confidence` number
      - `currency` string
      - `statistics` object
        - `median` number
        - `mean` number
        - `validSampleSize` integer
        - `priceRange` object
          - `min` number
          - `max` number
      - `insights` string[]
    - `images` object: Permanent S3 image URLs. Each filename is `{index}_{requestId}.jpg` — the leading index identifies position (0 is the hero) and the requestId makes the filename globally unique, so it is safe to key stored files on the URL basename.
      - `hero` string: Primary/hero image URL
      - `all` string[]: All image URLs in order
    - `metadata` object
      - `requestId` string
      - `processingTimeMs` integer
      - `version` string

## Other responses

- `202`: Async mode — job ID returned (poll GET /jobs/{id})
- `400`: Invalid request
- `401`: Unauthorized - missing or invalid token
- `402`: Monthly usage limit reached or billing blocked
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/shopfront/apis/shopfront-public-api.md) - [All operations](https://skmtc.dev/shopfront/apis/shopfront-public-api/llms.txt) - [OpenAPI document](https://skmtc.dev/shopfront/apis/shopfront-public-api/revisions/4dbfb90550d4?raw)
