# Submit a PDP by id

`PUT /v1/pdps/id/{id}`

Submitting a PDP by id sends its hero and carousel images for
scoring. If the submission changes the PDP, it will be processed and the
response is 202 with status PROCESSING. Use
[Retrieve a PDP by id](/api/product-details/getPdpById) to watch the
scores come in. If nothing has changed, the PDP won't be processed and
the response is 200 with status COMPLETED or ERROR.

The `id` in the path is your own stable identifier and is echoed back on
the corresponding GET so you can correlate records. Optionally supply
`gtin` or `asin` in the body to help Vizit resolve the product category;
they are used only for category resolution and are never echoed back.

Subsequent submissions are reconciled by filename: a filename already
on the PDP is reused (no re-download, no re-score), while a new
filename replaces the previous image in that slot. An identical
payload is a no-op.

Use this endpoint when you can supply your own image URLs (for
example, from a PIM). To have Vizit scrape an Amazon listing
instead, use [Submit a PDP by ASIN](/api/product-details/upsertPdpByAsin).

## Request Body

### application/json

- **object** — Provide at most one category: `product_category_id` (a Vizit category
UUID) or `external_category_id` (your own category identifier). Supplying
both is rejected. Neither is required — when both are omitted the category
is resolved from `gtin`, `asin`, or `name`.

  - `gtin` (any) — GTIN-8, GTIN-12 (UPC-A), GTIN-13 (EAN-13), or GTIN-14. Used only for
category resolution; it is never echoed back. Leniently handled — a
value that isn't 8/12/13/14 digits is ignored (treated as null), not
rejected.

  - `asin` (any) — Amazon ASIN — 10 uppercase alphanumeric characters. Used only for
category resolution; it is never echoed back. Leniently handled — a
value that isn't a valid ASIN is ignored (treated as null), not
rejected.

  - `hero_image_url` (string, required) — Publicly accessible URL to the hero image (HTTP/HTTPS, jpeg/png/webp, ≤25 MB)
  - `carousel_image_urls` (array<string>) — Ordered list of carousel image URLs. Order is preserved as carousel
position. Upsert is name-based: same filename as an existing image
on this PDP → reuse; different filename → replace.

    - items:
      - **string**
  - `product_category_id` (any) — Category UUID the PDP belongs to. Must be one returned by
[List product categories](/api/product-details/listProductCategories) —
if it doesn't exist or the organization lacks ICP access, the
request is rejected with `CATEGORY_NOT_FOUND`. Mutually exclusive with
`external_category_id`.

  - `external_category_id` (any) — Your own category identifier, resolved to a Vizit category on your
organization's behalf. Use instead of `product_category_id` when you
track categories under your own IDs. Mutually exclusive with
`product_category_id`.

  - `integration_id` (any) — Identifier of the integration this PDP belongs to (integration clients only).
  - `hero_image_asset_id` (any) — Partner-side asset identifier for the hero image, echoed back so you
can correlate the scored image with your own asset record.

  - `carousel_image_asset_ids` (array<string>) — Partner-side asset identifiers for the carousel images, positionally
aligned with `carousel_image_urls`. Must be empty or the same length
as `carousel_image_urls`.

    - items:
      - **string**
  - `name` (any) — Optional product display name.
  - `retailer` (any) — Retailer region identifier. Must be a value present in the retailer_regions table. Defaults to amazon_us.

## Responses

### 200 — Nothing changed, so the PDP won't be processed. The status is its
current value, COMPLETED or ERROR.


**application/json**

- **object**
  - `id` (string, required) — The caller-supplied identifier this PDP was submitted under.
  - `pdp_id` (string, required) — Internal identifier for the PDP (useful for support diagnostics)
  - `status` (enum: "PROCESSING" | "COMPLETED" | "ERROR", required) — The PDP's status after the submission. If the PDP changed and will be
processed, the status is PROCESSING and the response is 202. If nothing
changed, the PDP won't be processed and the status is its current value,
COMPLETED or ERROR, with a 200 response. Poll GET to follow a PROCESSING
PDP to its final status.

  - `error_code` (any) — Machine-readable error code when `status` is `"ERROR"`; `null`
otherwise.

  - `score_url` (string, required) — Relative path to poll for scoring results. Carries the `retailer`
query parameter so callers do not have to reconstruct it.


### 202 — The PDP changed and will be processed. The status is PROCESSING.


**application/json**

- **object**
  - `id` (string, required) — The caller-supplied identifier this PDP was submitted under.
  - `pdp_id` (string, required) — Internal identifier for the PDP (useful for support diagnostics)
  - `status` (enum: "PROCESSING" | "COMPLETED" | "ERROR", required) — The PDP's status after the submission. If the PDP changed and will be
processed, the status is PROCESSING and the response is 202. If nothing
changed, the PDP won't be processed and the status is its current value,
COMPLETED or ERROR, with a 200 response. Poll GET to follow a PROCESSING
PDP to its final status.

  - `error_code` (any) — Machine-readable error code when `status` is `"ERROR"`; `null`
otherwise.

  - `score_url` (string, required) — Relative path to poll for scoring results. Carries the `retailer`
query parameter so callers do not have to reconstruct it.


### 400 — Validation or image-download error. Check `error_code` to
distinguish:
  - INVALID_ID — id is empty or longer than 512 characters
  - INVALID_RETAILER — retailer is not in retailer_regions
  - NO_DEFAULT_PORTFOLIO — org has no default portfolio
  - CATEGORY_NOT_FOUND — the supplied product_category_id does not exist or is outside the org's ICP
  - EXTERNAL_CATEGORY_UNMAPPED — the supplied external_category_id could not be mapped to a Vizit category
  - CATEGORY_UNRESOLVED — no category was supplied and none could be inferred from gtin, asin, or name
  - IMAGE_DOWNLOAD_FAILED — one or more images could not be downloaded; `extra.failed_urls` contains the per-URL reason


**application/json**

- **object** — Standardized error format for all `/v1/*` endpoints. `error_code` is a
stable SCREAMING_SNAKE_CASE identifier clients can switch on.

  - `detail` (string) — Human-readable error message
  - `status_code` (integer) — HTTP status code
  - `error_code` (string) — Stable machine-readable error code
  - `request_id` (any) — Request identifier for correlation
  - `extra` (any) — Endpoint-specific additional context. For example,
IMAGE_DOWNLOAD_FAILED populates extra.failed_urls with per-URL
failure reasons so clients can triage bulk submissions.

  - `timestamp` (string)

### 401 — Missing or invalid Bearer token

### 413 — Too many carousel images (>20)

**application/json**

- **object** — Standardized error format for all `/v1/*` endpoints. `error_code` is a
stable SCREAMING_SNAKE_CASE identifier clients can switch on.

  - `detail` (string) — Human-readable error message
  - `status_code` (integer) — HTTP status code
  - `error_code` (string) — Stable machine-readable error code
  - `request_id` (any) — Request identifier for correlation
  - `extra` (any) — Endpoint-specific additional context. For example,
IMAGE_DOWNLOAD_FAILED populates extra.failed_urls with per-URL
failure reasons so clients can triage bulk submissions.

  - `timestamp` (string)

### 422 — Request validation failed (error_code VALIDATION_ERROR)

**application/json**

- **object** — Standardized error format for all `/v1/*` endpoints. `error_code` is a
stable SCREAMING_SNAKE_CASE identifier clients can switch on.

  - `detail` (string) — Human-readable error message
  - `status_code` (integer) — HTTP status code
  - `error_code` (string) — Stable machine-readable error code
  - `request_id` (any) — Request identifier for correlation
  - `extra` (any) — Endpoint-specific additional context. For example,
IMAGE_DOWNLOAD_FAILED populates extra.failed_urls with per-URL
failure reasons so clients can triage bulk submissions.

  - `timestamp` (string)

### 429 — Rate limit exceeded for this organization

### 502 — Image upload to S3 failed

**application/json**

- **object** — Standardized error format for all `/v1/*` endpoints. `error_code` is a
stable SCREAMING_SNAKE_CASE identifier clients can switch on.

  - `detail` (string) — Human-readable error message
  - `status_code` (integer) — HTTP status code
  - `error_code` (string) — Stable machine-readable error code
  - `request_id` (any) — Request identifier for correlation
  - `extra` (any) — Endpoint-specific additional context. For example,
IMAGE_DOWNLOAD_FAILED populates extra.failed_urls with per-URL
failure reasons so clients can triage bulk submissions.

  - `timestamp` (string)
