Skip to content
Coritan Docs

Upload and import your store's media

Upload images and PDFs, use them on products and in emails, and copy a WordPress media library with the paths it had.

View as Markdown

Media holds the images and PDFs your store serves. Each file has an address that anyone can open and that never changes, so a product, an email or another site can link to it. Upload files from your computer, or copy a WordPress site's media library into the store with the paths its files had, so that links to the old site keep working once your storefront takes over its domain.

  • Every member of the organization can open Media and look through the files. Uploading files, changing their alt text, deleting them and importing from WordPress need the Admin or Owner role.
  • You can upload JPEG, PNG, WebP, AVIF and GIF images of up to 25 MB, and PDFs of up to 50 MB. We read the type from the file itself, so renaming a file does not change it. SVG files cannot be uploaded.
  • A store can upload 120 files an hour and start 3 WordPress imports in 24 hours. Its files can take 5 GB in all, unless support set a different limit, as How much the store can hold explains.
  1. In the dashboard, open the organization, then Commerce, then Products, and choose Media in the row under it.
  2. Search by name or path. Choose All, Images or PDFs, and Any source, Uploaded here or From WordPress.
  3. To look in one folder, select it under Folders. The row above the files shows the folder you are in. Select All files to go back to the top.
  4. Choose Grid for thumbnails, or List for a table with each file's type, size, dimensions, the date it was added and where it came from.
  5. Select a file to open it in a panel over the list.

Files shows 48 files a page, newest first. The page address keeps the search, the filters, the folder, the layout and the page, so a reload or a shared link opens the list as you left it. Folders names only the folders that the files on the current page sit in. To reach another folder, search by its path.

  1. Open the folder you want the files in. At All files, they go into this month's folder, such as uploads/2026/10. The description of the Files card names the folder.
  2. Select Upload files… and choose one or more files, or drop them on the Files card.
  3. Uploads shows each file as it goes up, one at a time. You can leave the page while they upload. Closing the tab stops them, and the browser asks you first. Stop uploading stops every file still waiting.
  4. Each saved file shows its path. Select Open to see it.

A message then says how many files were uploaded, and Uploads says why for each file that was not. When the store refuses a file for a reason that applies to the rest, such as the hourly limit, the files after it are not sent. Clear the list empties Uploads once nothing is uploading.

A file's path is its folder and its name, such as uploads/2026/10/front-view.jpg. Its address is the Address of the files, under Storage and address, followed by a slash and the path.

  • We clean each name the way WordPress does: spaces become hyphens, accents and characters such as ?, # and & are dropped, and capital letters stay. Front View.JPG is saved as Front-View.JPG.
  • The extension follows the file's real type, so a PNG image named photo.jpg is saved as photo.png.
  • Paths are case-sensitive: Photo.jpg and photo.jpg are two files.
  • When the folder already has a file with that name, the new one gets -1, then -2 and so on before its extension, such as front-view-1.jpg.
  • A path never changes, and neither does the file at it. To change a picture, upload the new one and point the products that show the old one at it.
  • A folder exists while a file is in it. To upload into a new folder, name it in folder through the API (With the API). A folder has at most 10 levels and 255 characters.

In the product's Images card, select Add image…, then Choose from media to pick one of the store's images, or Upload to add one from your computer. Add images has the steps. A PDF cannot be a product image.

  1. Open the file, and select Copy the address next to Address. Open the file in a new tab shows it as anyone will see it.
  2. Paste the address where you need it: in an <img> tag in the HTML of one of the store's emails, on your storefront or on another site.

Anyone can open the address without signing in. A PDF opens in the browser, which suits a size guide or a manual.

Alt text describes an image to someone who cannot see it. Open the image, enter its Alt text, up to 255 characters, and select Save alt text. When you add the image to a product, the product image copies the alt text, unless you give it a Description of its own. Changing the alt text later leaves the product images that copied it as they are. A file imported from WordPress brings the alt text it had there.

  1. Open the file. Products that show it lists every product whose images or thumbnail link to it.
  2. While any product shows it, Delete file… is greyed out. Open each product and change or remove that image first.
  3. Select Delete file…, then Delete file to confirm.

The file's address stops working everywhere it is linked, such as in emails and on other sites. Its path is not used again: a new upload with the same name gets a number added, and an import skips the path.

Caution

Deleting a file cannot be undone. Upload it again to get it back, at a new path.

A product links to a file when one of its images has the file's address, or the file's path on your storefront's own address, such as https://shop.example.com/wp-content/uploads/2024/05/front.jpg. Your storefront's own addresses are its Storefront address and the Sites that may call the Store API in Settings.

Storage and address, under the files, shows how much of its limit the store's files take, and how many files it has. The limit is 5 GB, unless support set a different one for your store. Uploaded and imported files both count.

  • At 90% of the limit, Media opens with The store's media is almost full. At the limit, it opens with The store's media is full.
  • An upload past the limit is refused with This file would take the store's media past its 5 GB limit. Remove files you no longer use, or ask support to raise the limit.
  • An import stops at the limit. The files it copied stay, and its report says why it stopped.
  • When support lowers the limit below what the store holds, nothing is deleted. Uploads and imports stop until enough files are deleted.

To make room, delete files the store no longer uses. To ask for a higher limit, open a ticket.

An import copies a WordPress site's media library into the store: each file, every smaller copy WordPress made of it, and the original of a large image WordPress scaled down. Each file keeps the path it had on the site, such as wp-content/uploads/2024/05/photo.jpg, so its new address ends the way its old one did.

  • The site must be online at an https:// address that anyone can reach. The import reads its media library through the WordPress REST API.
  • An import can copy the files of drafts and private posts too. It then signs in to the site as a WordPress user who can edit media, with an application password you create for it.
  • Large libraries take a while. The import works on our side, so you can close the dashboard while it runs.

Create an application password

Section titled Create an application password

You need one only to include the files of drafts and private posts, or when the site shows its media library only to signed-in users.

  1. Sign in to WordPress as a user who can edit media, such as an administrator.
  2. Open Users > Profile, and find Application Passwords.
  3. Give the new password a name you will recognise, such as Coritan import, and add it.
  4. Copy the password. WordPress shows it only once.

We keep the password sealed while the import runs, and erase it when the import ends, however it ends. No page or API answer shows it. Revoke it in WordPress under Users > Profile > Application Passwords once the import has finished.

  1. In Media, select Import from WordPress….
  2. Enter the Site address, such as https://example.com, or https://example.com/blog for a site in a folder.
  3. Optionally, tick Also scan the site's pages for files. The import then also reads the home page and the pages the site's sitemap lists, 500 pages at most, and copies the files they link to that the media library does not list.
  4. To include the files of drafts and private posts, tick Include files attached to unpublished pages. Enter the WordPress user name and the Application password.
  5. Select Start import.

The import's report opens and follows the import on its own. While the import runs, Media shows its progress at the top. See its progress opens the report, and Show the files it has copied refreshes the list. A store runs one import at a time, so Import from WordPress… waits while another import runs.

WordPress imports, under the files in Media, lists the store's imports, newest first, each with its status: Waiting to start, Importing, Finished, Failed or Cancelled. Select one to open its report. All media goes back to the list.

While the import runs, the report shows Waiting to start, then Copying the media library with how many media items it has read, then Scanning the site's pages when the page scan is on. Each file shows in Media as soon as it is copied.

The counts are So far while the import runs, What it did once it has finished, and What it did before it stopped when it failed or was cancelled:

Count What it counts
Media items read The items of the media library it has read. An item holds several files: the upload, each smaller copy and the original of a scaled image.
Files copied The files it put in the store.
Already in the store The files it skipped because the store has them already, from the same address or with the same content, and the files you deleted from the store.
Could not copy The files it left out, listed under Files it could not copy.
Copied in all The size of the files it copied.
Pages scanned With the page scan, the pages it read.
Pages it could not read With the page scan, the pages that did not answer.

Files it could not copy lists each file it left out, with its address, the path it would have had and why. Choose a reason under Every reason to see one kind, search the list, or select Download as CSV to save all of them. The report lists the first 1,000 files and counts the rest.

Reason API reason What it means
Not on the site not_found The site has no file at that address.
The site answered with an error status The site answered with an error for that file. Run the import again later.
Over 50 MB too_large The file is larger than an import copies.
Not a file the store takes type The file is a video, a sound, a document other than a PDF, or a file that cannot be opened.
Empty file empty The site sent an empty file.
The site was too slow timeout The site took too long to send the file. Run the import again.
The site could not be reached unreachable The connection failed. Run the import again later.
Sent to another address redirect The file redirects to another site, or to an http:// address.
Private address refused The file is on a private network, or on a port the import does not use.
Path already taken path_taken The store already has a different file at that path, and keeps it.
Outside the uploads folder outside_uploads The file is not in the site's wp-content/uploads folder.
Path not allowed bad_path The path is too long, or holds characters a path cannot hold.

To get a file the import left out, upload it yourself.

What the import copies and what it cannot see

Section titled What the import copies and what it cannot see

The import copies every file the media library lists, with each smaller copy and the original of a scaled image, and the alt text each image has in WordPress. It copies files of up to 50 MB: images, PDFs and SVG images. An SVG image is served so that no script in it can run. It cannot see:

  • The files of drafts and private posts, unless it signs in with an application password.
  • Files that are in wp-content/uploads but not in the media library, such as those a plugin put there, unless the page scan is on and a page links to them. The scan reads the home page and the pages listed by the sitemap at /wp-sitemap.xml. When the site has no sitemap there, the scan reads only the home page, and the report says so.
  • Anything outside wp-content/uploads, such as a theme's images. A media library item kept somewhere else, for example by a plugin that moves uploads to cloud storage, is listed as Outside the uploads folder.
  • Files on another site. The import does not follow a file that redirects to another site, other than the same site with or without www..

The page scan counts a link through Jetpack's image CDN or ExactDN, or a link to the site on www. or over http://, as a link to the file on the site, and copies the file from the site.

  • Run the import again…, on the report of an import that has ended, opens the import with the same settings. Enter the application password again, since we never show it. The new import copies only what is new: it skips each file the store has from the same address or with the same content, and a file you deleted stays deleted.
  • Cancel import…, on the report of a running import, stops it. Select Cancel import to confirm, or Keep importing. The files it copied stay in Media, and running it again later copies the rest.

Once the store has your WordPress files, your storefront can serve them at their old addresses. Links to them in old emails, pages and feeds then keep working after your storefront takes over the site's domain.

  1. In Media, copy the Address of the files under Storage and address, such as https://api.coritan.com/media/stores/42.
  2. On your storefront, rewrite each request for a path under /wp-content/uploads/ to that address, with the same path after it. Do the same for /uploads/, so the files you upload in Media show on your domain too.
  3. Before you move the domain, open a few old file addresses on the storefront's current address, and check that each shows the same file as WordPress does.

With the rewrites, https://example.com/wp-content/uploads/2024/05/photo.jpg serves the file at https://api.coritan.com/media/stores/42/wp-content/uploads/2024/05/photo.jpg. A storefront on Next.js, such as the starter, adds them to its next.config.ts:

TypeScript
const MEDIA = "https://api.coritan.com/media/stores/42";

const nextConfig: NextConfig = {
  async rewrites() {
    return [
      { source: "/wp-content/uploads/:path*", destination: `${MEDIA}/wp-content/uploads/:path*` },
      { source: "/uploads/:path*", destination: `${MEDIA}/uploads/:path*` },
    ];
  },
};

Keep the WordPress site online at another address until the move is done, so you can run the import again for anything it missed.

  • Each file you upload or import is in Media, at an address that anyone can open and that never changes.
  • A product image chosen from Media shows on the storefront from that address.
  • Each import's report shows what it copied, and lists every file it could not copy with the reason.
Upload files… and Import from WordPress… are missing
Your role is below Admin, so Media is read-only for you. Ask an owner or admin.
SVG files cannot be uploaded.
Save the image as PNG or WebP and upload that. An import copies the SVG images a WordPress media library lists.
That file type is not supported.
Upload a JPEG, PNG, WebP, AVIF or GIF image, or a PDF. We read the type from the file's content, so a file renamed to .jpg is refused when it is not an image.
The image is 31.2 MB. An image can be at most 25 MB.
Make the image smaller and upload it again. A PDF can be up to 50 MB.
A store can upload 120 files an hour. Try again later.
The store reached the hourly limit, and the files after it were not sent. Upload them again later.
This file would take the store's media past its 5 GB limit.
Delete files the store no longer uses, or open a ticket to ask for a higher limit, as How much the store can hold explains.
Delete file… is greyed out
A product shows the file. Products that show it names each one: change or remove its image, then delete the file.
No WordPress media library answered at that address. Check the site address.
Check the Site address. Give the address of the WordPress site itself, with its folder if it has one, such as https://example.com/blog.
The site lists its media library only to a signed-in user.
Run the import again with Include files attached to unpublished pages ticked, and a user name and application password.
WordPress refused the username and application password, or that user cannot edit media.
Check the user name and the password, and that the user can edit media. Create a new application password if you are not sure of it.
The site sends its media library to example.org. Use https://example.org as the site address.
The site's address has changed. Start the import with the address the message gives.
Another import is running. One import runs at a time; start this one when it finishes.
Wait for that import to end, or cancel it from its report.
A store can start 3 media imports in 24 hours. Try again later.
Wait, then run the import again. It copies only what is new.
The import stopped at the store's limit
Delete files the store no longer uses, or ask support to raise the limit, then select Run the import again…. It skips the files it has already copied.
An old link shows nothing after the move
Check that the storefront rewrites the path to the Address of the files. Then search Media for the path: if the file is not there, look for it under Files it could not copy in the import's report.

Every route is under https://api.coritan.com/api/v1/orgs/{org_slug}/commerce/media. Reading the files and the imports takes the commerce.catalog:read scope, or any member's token. Uploading, changing alt text, deleting and importing take commerce.media:write, or an owner's or admin's token. commerce.media:write does not include reading, so a key that also lists the files needs both scopes.

Upload a file with POST /commerce/media, as multipart/form-data with the field file. folder is optional: without it, the file goes into this month's uploads/YYYY/MM, and an empty value puts it at the top. alt is optional too.

Shell
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/commerce/media" \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -F "file=@front-view.jpg" \
  -F "folder=products/2026" \
  -F "alt=The front, with the two network ports on the left"

The answer is 201 with the file:

JSON
{
  "media": {
    "id": "media_01j9z8q4n6xk3v7d2m5r8t1wqa",
    "path": "products/2026/front-view.jpg",
    "url": "https://api.coritan.com/media/stores/42/products/2026/front-view.jpg",
    "content_type": "image/jpeg",
    "bytes": 482113,
    "width": 1600,
    "height": 1200,
    "alt": "The front, with the two network ports on the left",
    "source": "upload",
    "source_url": null,
    "created_at": "2026-10-07T09:12:44Z"
  }
}
  • GET /commerce/media lists the files, newest first. It takes q (the path holds this text, in any case), type (image or pdf), source (upload or wordpress), folder (that folder and the folders inside it), limit (1–200, 50 by default) and offset. The answer has media, count, usage with the store's bytes, files and quota_bytes, and base_url, the Address of the files.
  • GET /commerce/media/{media_id} answers the file and used_by, the products whose images use it, each with product_id and title.
  • PATCH /commerce/media/{media_id} with {"alt": "..."} changes the alt text. null or an empty value clears it. Nothing else about a file can change.
  • DELETE /commerce/media/{media_id} deletes the file and answers 204.
  • POST /commerce/products/{product_id}/images takes media_id in place of url, as Add products describes. The image then links to the file's url, and takes its width, height and alt text unless you send them.

Start an import with POST /commerce/media/imports. Send username and application_password together, or neither:

Shell
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/commerce/media/imports" \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"source": "wordpress", "site_url": "https://example.com", "scan_pages": true,
       "username": "alex", "application_password": "'"$WP_APP_PASSWORD"'"}'

The answer is 202 with the import, whose status is queued. Follow it with GET /commerce/media/imports/{import_id} until status is done, failed or canceled. The import has the counts the report shows (items_seen, files_copied, files_skipped, files_failed, bytes_copied, pages_scanned, pages_failed), error when it failed, and failures, each with its url, path, reason and message. failures_more counts the failures beyond the first 1,000. GET /commerce/media/imports lists the imports without their failures, and takes status, limit (1–100, 20 by default) and offset. POST /commerce/media/imports/{import_id}/cancel stops a queued or running import.

Status error When
413 quota_exceeded The file would take the store past its limit. quota_bytes and used_bytes give the numbers.
413 payload_too_large An image is over 25 MB, or a PDF over 50 MB.
415 unsupported_media_type The file is not a JPEG, PNG, WebP, AVIF, GIF or PDF, it is an SVG, or the body is not multipart/form-data.
409 in_use Products use the file you are deleting. products lists them.
409 import_in_progress Another import of the store is queued or running. import_id names it.
409 import_ended The import you are cancelling has already ended.
422 invalid A field the route cannot use, such as a site_url that is not https://. field names it.
429 rate_limited The store uploaded 120 files in the last hour, or started 3 imports in 24 hours. Retry-After says when to try again.
503 storage_unavailable Storage cannot take files right now. Try again in a few minutes.

API operations on this page

MethodPathWhat it does
GET/api/v1/orgs/{org_slug}/commerce/mediaThe store's media, newest first
POST/api/v1/orgs/{org_slug}/commerce/mediaUpload an image or a PDF to the store's media, and answer 201 with the file
GET/api/v1/orgs/{org_slug}/commerce/media/importsThe store's media imports, newest first, without their lists of failures
POST/api/v1/orgs/{org_slug}/commerce/media/importsStart media import
GET/api/v1/orgs/{org_slug}/commerce/media/imports/{import_id}One media import: its state, its counts and the files it could not copy
POST/api/v1/orgs/{org_slug}/commerce/media/imports/{import_id}/cancelCancel a queued or running import, and answer it
GET/api/v1/orgs/{org_slug}/commerce/media/{media_id}One file of the store's media, and the products whose images use it
PATCH/api/v1/orgs/{org_slug}/commerce/media/{media_id}Change a file's alt text, and answer the file
DELETE/api/v1/orgs/{org_slug}/commerce/media/{media_id}Delete a file from the store's media, and answer 204