# Images

## Filter Images

 - [GET /images](https://docs.api.saleslayer.com/apis/dam-v2.0/images/paths/~1images/get.md): Returns a paginated collection of the tenant's images, filtered and sorted using OData query options ($filter, $orderby, $top, $skip).
            
## Authentication
            
Requires a valid API key in the X-API-KEY header.
            
## Response (200 OK)
            
The page is wrapped in the house collection envelope — the images are under value, never at the
top level:
            
json
{
  "value": [
    {
      "id": 42,
      "reference": "product-main-front.jpg",
      "numLinks": 3,
      "status": "Ok",
      "modifiedOn": "2024-04-05T08:44:33",
      "fileType": "jpg",
      "createdOn": "2024-04-05T08:44:33",
      "tags": ["animales", "mascotas"],
      "originalUrl": "https://cdn.example.com/product-main-front.jpg",
      "thumbnailUrl": "https://cdn.example.com/product-main-front_TH.jpg",
      "thumbnailMediumUrl": "https://cdn.example.com/product-main-front_THM.jpg",
      "thumbnailPreviewUrl": "https://cdn.example.com/product-main-front_THP.jpg",
      "width": 800,
      "height": 600,
      "sizeInBytes": 102400
    }
  ],
  "@count": 137,
  "@readLink": "https://api2.saleslayer.com/dam/images?$top=50",
  "@nextLink": "https://api2.saleslayer.com/dam/images?$top=50&$skip=50"
}

            
Notes on the payload:
            
- status tells you whether the image is usable yet — see the status values documented on
  GET /images. A freshly created or substituted image is Up until the worker finishes.
- width, height and sizeInBytes come from the stored metadata and are 0 until processing
  has recorded them; the thumbnail URLs are empty until the renditions exist.
- tags is stored as a comma-separated string, so a tag that itself contains a comma comes back
  split into several tags.
- @count is the total number of matches, ignoring $top/$skip.
- @readLink is the canonical URL of this collection — this request, echoed back.
- @nextLink is the URL of the next page and is omitted on the last page.
            
The @-prefixed hypermedia links (@readLink, @nextLink) are only emitted when hypermedia
enrichment is configured for the deployment; @count is always present. Where the links are
absent, page forward by incrementing $skip yourself until fewer than $top items come back.
            
## Supported fields
            
Only these fields can be used in $filter and $orderby:
            
id, reference, numLinks, status, modifiedOn, fileType, createdOn
            
Field names are case-insensitive. Every other field of the response — including tags,
width, height, sizeInBytes and the URL fields — is not queryable: naming one in
$filter or $orderby returns 400. $orderby honours only the first sort expression.
            
$select and $expand are not supported and are ignored.
            
## Supported operators and functions
            
- Comparison: eq, ne, gt, ge, lt, le
- Logical: and, or, not
- Sets: in
- Functions: contains, startswith, endswith
            
## Status field values
            
- Vd - Void (not yet started processing)
- Up - Updating (being uploaded/updated)
- Ok - Processed correctly (ready to use)
- Re - Reprocessing
- Er - Error (processing failed)
- Dv - Deleted/void (no longer counts towards the tenant's image-library quota)
            
Results are not filtered by status, so images in any of these states can be returned. Filter on
status explicitly if you only want usable images.
            
## Examples
            

GET /images?$filter=fileType eq 'jpg'
GET /images?$filter=status eq 'Ok'
GET /images?$filter=fileType eq 'png' and status eq 'Ok'
GET /images?$filter=status in ('Ok', 'Re')
GET /images?$filter=contains(reference, 'product')
GET /images?$filter=startswith(reference, 'product-') and numLinks gt 0
GET /images?$orderby=createdOn desc
GET /images?$filter=fileType ne 'gif'&$orderby=numLinks desc&$skip=0&$top=50

            
## Error Responses
            
- 400 Bad Request: Invalid $filter syntax, a field that is not queryable in $filter or
  $orderby, a negative $skip, or a $top that is not between 1 and 100
- 401 Unauthorized: Missing or invalid API key. Returned by the API gateway as
  { "message": "Unauthorized", "request_id": "d8aafa5b8f3e400b60bea0123dd33317" }
- 403 Forbidden: The API key does not have read permissions for this operation. Same body
  shape as the 401
- 500 Internal Server Error: Technical error (e.g. database unavailable)
            
A 400 uses the shared validation envelope, keyed by the offending parameter:
            
json
{
  "validationFailures": {
    "$orderby": [
      {
        "PropertyName": "$orderby",
        "ErrorMessage": "Ordering by 'tags' is not supported. Supported fields: id, reference, numlinks, status, modifiedon, filetype, createdon",
        "AttemptedValue": null
      }
    ]
  }
}

## Create Image

 - [POST /images](https://docs.api.saleslayer.com/apis/dam-v2.0/images/paths/~1images/post.md): Registers a new image from a publicly accessible URL, optionally tagging it. The image is downloaded, validated against the supported formats and size limits, and processed asynchronously in the background.
            
## Authentication
            
Requires a valid API key in the X-API-KEY header.
            
## Request Body
            
json
{
  "url": "https://example.com/images/product.jpg",
  "tags": ["ecommerce", "catalog", "summer-2024"]
}

            
### URL Field (Required)
            
The URL must meet the following requirements:
            
Valid URL formats:
- Must be a valid HTTP or HTTPS URL
- Must be absolute (e.g., https://example.com/image.jpg)
- Relative URLs are NOT supported (e.g., /images/photo.jpg)
- The filename will be automatically extracted from the URL path
            
Examples of valid URLs:

https://example.com/product.jpg
https://cdn.example.com/images/2024/photo.png
http://assets.example.com/catalog/item_123.gif

            
Examples of invalid URLs:

/images/product.jpg                    (relative URL)
example.com/image.jpg                   (missing protocol)
ftp://server.com/file.jpg              (FTP not supported)
https://example.com/page.html          (not a supported image extension)
https://example.com/product            (no extension to derive the file type from)

            
URL validation rules:
- Maximum length: 2048 characters
- The URL must be publicly accessible (no authentication supported)
- The host must resolve to a public address. URLs pointing at loopback, private ranges or the
  cloud metadata endpoint are rejected
- The URL is probed with a HEAD request; anything other than a 2xx is rejected
            
### Tags Field (Optional)
            
Optional array of strings to categorize and organize your images.
            
Rules:
- Each tag cannot be empty or contain only whitespace
- Tags are case-sensitive
- Tags are stored as a single comma-separated string, so a tag containing a comma is read back as
  several tags. Avoid commas inside a tag
- There is no explicit limit on the number of tags, but the whole comma-separated list must fit in
  64 KB of storage
            
Example:
json
{
  "url": "https://example.com/logo.png",
  "tags": ["branding", "corporate", "logo", "2024"]
}

            
## Supported Image Formats
            
The file type is taken from the URL's file extension, and the Content-Type header returned by the
URL is validated independently against the list of accepted image media types:
            
### Fully Supported Formats
            
| Format | Extension | Accepted Content-Type | Notes |
|--------|-----------|-----------------------|-------|
| JPEG | .jpg, .jpeg | image/jpeg | Standard and progressive JPEG |
| PNG | .png | image/png | PNG-8, PNG-24, PNG-32 with transparency |
| GIF | .gif | image/gif | Static and animated GIF |
| TIFF | .tif, .tiff | image/tiff, image/x-tiff | Single and multi-page TIFF |
| WebP | .webp | image/webp | Lossy and lossless compression |
| PSD | .psd | image/vnd.adobe.photoshop, image/photoshop, image/psd, application/octet-stream | Adobe Photoshop files |
            
### Content-Type Validation
            
1. The URL must return a Content-Type header
2. The Content-Type must be one of the accepted media types listed above
3. application/octet-stream is only accepted for .psd files — many servers are misconfigured
   and return this generic type for Photoshop files
4. Apart from that application/octet-stream rule, the Content-Type is not cross-checked
   against the file extension: a URL ending in .jpg that serves image/png is accepted, and the
   image is registered with file type jpg
            
Examples of valid Content-Type responses:

Content-Type: image/jpeg          → ✅ Accepted
Content-Type: image/png           → ✅ Accepted
Content-Type: image/gif           → ✅ Accepted
Content-Type: application/octet-stream with .psd extension → ✅ Accepted

            
Examples of invalid Content-Type responses:

Content-Type: text/html           → ❌ Rejected (not an image)
Content-Type: application/pdf     → ❌ Rejected (not supported)
Content-Type: application/octet-stream with .jpg extension → ❌ Rejected
(no Content-Type header)          → ❌ Rejected (missing header)

            
## Image Size and Weight Limits
            
The system automatically validates downloaded images:
            
### File Size Limits
- Maximum file size: 250 MB (262,144,000 bytes)
- Files larger than this limit will be rejected with a validation error
            
### Dimension Limits
- Maximum width: 30,000 pixels
- Maximum height: 30,000 pixels
- Images exceeding these dimensions will be rejected
            
## Response
            
### Success Response (201 Created)
            
Returns the reference (filename) of the created image, wrapped in the house entity envelope. The
reference is the public, tenant-unique key; the numeric id is internal only and is not exposed:
            
json
{
  "value": {
    "reference": "product-123.jpg"
  },
  "@readLink": "https://api2.saleslayer.com/dam/images",
  "@editLink": "https://api2.saleslayer.com/dam/images"
}

            
A Location header points at the new resource, as an absolute URL —
https://api2.saleslayer.com/dam/images(product-123.jpg).
            
Note that on a create, the @readLink/@editLink annotations echo the URL this request was posted
to — the collection, not the new image. Use the Location header to address the created resource.
They are only emitted when hypermedia enrichment is configured for the deployment.
            
The image will be processed asynchronously in the background: it is created with status Up and
the worker moves it to Ok or Er, filling in the dimensions and thumbnail URLs.
            
To check the processing status and retrieve full image details:

GET /images(product-123.jpg)

            
## Error Responses
            
### 400 Bad Request
            
Validation failures are returned in the shared envelope. Service-level messages carry no field
context, so they are grouped under a generic error key:
            
json
{
  "validationFailures": {
    "error": ["URL is required"]
  }
}

            
The messages you can receive, verbatim:
            
| Cause | Message |
|-------|---------|
| Missing or empty URL | URL is required |
| URL over 2048 characters | URL exceeds maximum length of 2048 characters |
| Not an absolute URL | URL is not valid |
| Scheme other than HTTP/HTTPS | URL must use HTTP or HTTPS protocol |
| No extension in the URL path | Could not determine file type from URL. Please ensure the URL contains a filename with an extension. |
| Unsupported extension | File extension 'pdf' is not allowed. Allowed extensions: jpg, jpeg, png, gif, tif, tiff, psd, webp |
| Host is not publicly routable | URL host 'localhost' is not allowed. |
| URL not reachable (non-2xx HEAD) | URL is not accessible. HTTP status: 404 Not Found |
| No Content-Type header | URL does not return a valid Content-Type header |
| application/octet-stream for a non-PSD file | URL returns generic Content-Type (application/octet-stream) for non-PSD file. Extension detected: jpg |
| Unsupported Content-Type | URL does not return an image Content-Type. Received: text/html. Allowed types: image/jpeg, image/png, ... |
| Empty file | The uploaded file is empty |
| File too large | Image file size (300.00 MB) exceeds maximum allowed size of 250 MB |
| Dimensions unreadable | Could not read image dimensions from the uploaded file. Ensure it is a valid image in a supported format. |
| Image too wide | Image width (35000px) exceeds maximum allowed width of 30000px |
| Image too tall | Image height (35000px) exceeds maximum allowed height of 30000px |
| Empty or whitespace-only tag | Tags cannot contain empty values |
            
### 401 Unauthorized
            
Missing or invalid API key in the X-API-KEY header. Emitted by the API gateway:
            
json
{
  "message": "Unauthorized",
  "request_id": "d8aafa5b8f3e400b60bea0123dd33317"
}

            
Quote request_id when contacting support about a rejected request.
            
### 403 Forbidden
            
The API key does not have write permissions for this operation. Same body shape as the 401.
            
### 409 Conflict
            
Two distinct causes, both using the conflict envelope:
            
An image with the same filename (reference) already exists for this tenant:
            
json
{
  "error": "An image with the filename 'product-123.jpg' already exists"
}

            
The tenant has reached its image-library quota:
            
json
{
  "error": "Image library limit reached. Current: 5000, Maximum: 5000"
}

            
Images in status Dv (deleted) do not count towards the quota. Delete unused images to free
capacity, or contact Sales Layer to raise the limit.
            
### 500 Internal Server Error
            
Technical errors such as:
- Network connectivity issues reaching the source URL, or a download timeout (30s)
- Storage service unavailable
- Database errors
            
Note that a source URL that responds with a non-2xx status is a 400, not a 500 — only transport
failures and timeouts land here. The body identifies the request for support:
            
json
{
  "error": "Please contact the administrator of this application by supplying the following code: 0HNF1A2B3C4D5"
}

            
## Complete Example
            
Request:
http
POST /images HTTP/1.1
Host: api2.saleslayer.com
X-API-KEY: your-api-key-here
Content-Type: application/json
            
{
  "url": "https://cdn.example.com/products/summer-2024/product-123.jpg",
  "tags": ["ecommerce", "summer-collection", "featured", "new-arrival"]
}

            
Success Response:
http
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api2.saleslayer.com/dam/images(product-123.jpg)
            
{
  "value": {
    "reference": "product-123.jpg"
  }
}

            
Checking the result:
http
GET /images(product-123.jpg) HTTP/1.1
Host: api2.saleslayer.com
X-API-KEY: your-api-key-here

            
Result after processing:
json
{
  "value": {
    "id": 12345,
    "reference": "product-123.jpg",
    "status": "Ok",
    "fileType": "jpg",
    "width": 1920,
    "height": 1080,
    "sizeInBytes": 245760,
    "numLinks": 5,
    "tags": ["ecommerce", "summer-collection", "featured", "new-arrival"],
    "originalUrl": "https://cdn.example.com/product-123.jpg",
    "thumbnailUrl": "https://cdn.example.com/product-123_TH.jpg",
    "thumbnailMediumUrl": "https://cdn.example.com/product-123_THM.jpg",
    "thumbnailPreviewUrl": "https://cdn.example.com/product-123_THP.jpg",
    "createdOn": "2024-04-02T10:30:00",
    "modifiedOn": "2024-04-02T10:30:15"
  }
}

## Get Image

 - [GET /images({reference})](https://docs.api.saleslayer.com/apis/dam-v2.0/images/paths/~1images(%7Breference%7D)/get.md): In DAM, images are identified by their filename (reference), which is unique per tenant.
Use this endpoint to retrieve a specific image when you already know its filename.
            
## Authentication
            
Requires a valid API key in the X-API-KEY header.
            
## Example
            

GET /images(product-main-front.jpg)
X-API-KEY: your-api-key-here

            
## Response (200 OK)
            
The image is wrapped in the house entity envelope — its fields are under value, never at the
top level:
            
json
{
  "value": {
    "id": 42,
    "reference": "product-main-front.jpg",
    "numLinks": 3,
    "status": "Ok",
    "modifiedOn": "2024-04-05T08:44:33",
    "fileType": "jpg",
    "createdOn": "2024-04-05T08:44:33",
    "tags": ["animales", "mascotas"],
    "originalUrl": "https://cdn.example.com/product-main-front.jpg",
    "thumbnailUrl": "https://cdn.example.com/product-main-front_TH.jpg",
    "thumbnailMediumUrl": "https://cdn.example.com/product-main-front_THM.jpg",
    "thumbnailPreviewUrl": "https://cdn.example.com/product-main-front_THP.jpg",
    "width": 800,
    "height": 600,
    "sizeInBytes": 102400
  },
  "@readLink": "https://api2.saleslayer.com/dam/images(product-main-front.jpg)",
  "@editLink": "https://api2.saleslayer.com/dam/images(product-main-front.jpg)"
}

            
Notes on the payload:
            
- status tells you whether the image is usable yet — see the status values documented on
  GET /images. A freshly created or substituted image is Up until the worker finishes.
- width, height and sizeInBytes come from the stored metadata and are 0 until processing
  has recorded them; the thumbnail URLs are empty until the renditions exist.
- tags is stored as a comma-separated string, so a tag that itself contains a comma comes back
  split into several tags.
- @readLink and @editLink are the canonical URL of this image (this request, echoed back), and
  are only emitted when hypermedia enrichment is configured for the deployment.
            
## Error Responses
            
- 401 Unauthorized: Missing or invalid API key. Returned by the API gateway as
  { "message": "Unauthorized", "request_id": "d8aafa5b8f3e400b60bea0123dd33317" }
- 403 Forbidden: The API key does not have read permissions for this operation. Same body
  shape as the 401
- 404 Not Found: No image with the given reference exists for this tenant. The body is the
  message string, e.g. "Image with reference 'missing.jpg' not found"
- 500 Internal Server Error: Technical error (e.g. database unavailable)

## Patch Image

 - [PATCH /images({reference})](https://docs.api.saleslayer.com/apis/dam-v2.0/images/paths/~1images(%7Breference%7D)/patch.md): Partially updates an existing image identified by its filename (reference), which is unique per tenant — substitute its file via a new url and/or replace its tags. At least one of these fields must be supplied.
            
The filename at the end of the replacement url must be exactly the image's current reference —
same name and same extension (compared case-sensitively). Anything else is rejected with 400 and the
image is left untouched.
            

PATCH /images(product-123.jpg)
{ "url": "https://cdn.example.com/products/v2/product-123.jpg" }   OK   same filename
{ "url": "https://cdn.example.com/products/product-123-v2.jpg" }   400  different name
{ "url": "https://cdn.example.com/products/product-123.png" }      400  different file type

            
### Replacing an image with a differently named file today
            
While the limitation is in force, do it client-side in three steps:
            
1. Create the new image — POST /images with the new file's url.
2. Repoint the references — use the Catalog API to find every entity (product, variant, category, …)
   that uses the old filename and update it to the new one.
3. Delete the old image — DELETE /images({old-reference}) once nothing references it any more.
            
## Fields
            
- url (optional): public URL of the new image. Triggers a full image substitution. Must end in the
  image's current filename — see the temporary limitation above.
- tags (optional): array of strings, validated exactly as on POST /images.
  - Absent, and url not supplied either → tags are not modified.
  - Absent, url also supplied → all tags are cleared (legacy behaviour: substituting the
    image file wipes its tags unless you resend them in the same request).
  - [] → all tags are cleared.
  - ["a","b"] → tags are replaced with these values.
            
## Valid combinations
            
| Body | Operation |
|------|-----------|
| { "url": "https://.../{same-filename}" } | Substitute image — clears existing tags |
| { "url": "https://.../{same-filename}", "tags": ["a"] } | Substitute image and set tags |
| { "tags": ["a"] } | Update tags only, image file unchanged |
            
## Invalid combinations
            
- Empty body {} → 400.
- url whose filename differs from the image's reference → 400 (see the temporary limitation above).
            
## Response (204 No Content)
            
Success returns no body.
            
## Notes
            
- If the image is currently being processed, returns 409.
- A substitution resets the image's status to Up and re-enqueues it for processing, so the
  thumbnails are regenerated. Until the worker finishes, GET /images({reference}) reports Up
  and the rendition URLs still point at the previous file. Poll status for Ok (or Er) before
  treating the new file as live.
- A tags-only update does not touch status and takes effect immediately.
- Either way modifiedOn is refreshed.
- A substitution does not change the image's reference, so entities referencing it keep working.
            
## Error Responses
            
- 400 Bad Request: Invalid request body (e.g., both fields missing), a replacement url whose
  filename is not the image's current reference, or any of the url/tags validation failures
  documented on POST /images (format, accessibility, Content-Type, size, dimensions). Same
  { "validationFailures": { "error": [...] } } envelope
- 401 Unauthorized: Missing or invalid API key. Returned by the API gateway as
  { "message": "Unauthorized", "request_id": "d8aafa5b8f3e400b60bea0123dd33317" }
- 403 Forbidden: The API key does not have write permissions for this operation. Same body
  shape as the 401
- 404 Not Found: No image with the given reference exists for this tenant. The body is the
  message string, e.g. "Image with reference 'missing.jpg' not found"
- 409 Conflict: The image is currently being processed and cannot be modified —
  { "error": "Image 'product-123.jpg' is currently being processed and cannot be modified" }
- 500 Internal Server Error: Technical error (network failure fetching the replacement file,
  storage or database unavailable). The already-uploaded replacement object is cleaned up, so the
  image is left on its previous file

## Delete Image

 - [DELETE /images({reference})](https://docs.api.saleslayer.com/apis/dam-v2.0/images/paths/~1images(%7Breference%7D)/delete.md): Deletes an image by its filename (reference), which is unique per tenant.
            
## Authentication
            
Requires a valid API key in the X-API-KEY header.
            
## Example
            

DELETE /images(product-main-front.jpg)
X-API-KEY: your-api-key-here

            
## Response (204 No Content)
            
Success returns no body.
            
## Important: deletion does not cascade
            
The image record is removed outright. The deletion is not propagated to the entities that use
the image — check numLinks on the image first (GET /images({reference})): if it is greater than
zero, the products, variants and categories still referencing the filename will be left pointing at
an image that no longer exists. Repoint them via the Catalog API before deleting.
            
## Error Responses
            
- 401 Unauthorized: Missing or invalid API key. Returned by the API gateway as
  { "message": "Unauthorized", "request_id": "d8aafa5b8f3e400b60bea0123dd33317" }
- 403 Forbidden: The API key does not have write permissions for this operation. Same body
  shape as the 401
- 404 Not Found: No image with the given reference exists for this tenant. The body is the
  message string, e.g. "Image with reference 'missing.jpg' not found"
- 409 Conflict: Image is currently being processed and cannot be deleted —
  { "error": "Image 'product-123.jpg' is currently being processed and cannot be deleted" }
- 500 Internal Server Error: Technical error (e.g. database unavailable)

