Media
Uploaded images, videos and PDFs with named smaller versions, alt text per language, and replacement
A media record is one public file in the store’s library: an image, a video or a PDF. An image
comes with four named smaller versions, always WebP. Every file carries its public url; storage
keys are never exposed.
import type { EpochMilliseconds } from "arky-sdk";
interface Media {
id: string;
store_id: string;
file_name: string;
alt: Record<string, string> | null;
generation_id: string;
content:
| {
type: "image";
format: "jpeg" | "png" | "webp" | "gif";
original: MediaImage;
thumbnail: MediaImage;
small: MediaImage;
medium: MediaImage;
large: MediaImage;
}
| { type: "video"; format: "mp4" | "webm" | "quicktime"; original: MediaFile }
| { type: "pdf"; original: MediaFile };
status: { type: "active" } | { type: "deleting" };
created_at: EpochMilliseconds;
updated_at: EpochMilliseconds;
}
interface MediaFile {
size_bytes: number;
sha256: string;
url: string;
}
interface MediaImage extends MediaFile {
width_px: number;
height_px: number;
}
Upload
/v1/stores/{store_id}/media/{id} sdk.media.create() The app picks the media id and sends exactly one file or one source URL, as multipart form data:
const media = await sdk.media.create({
store_id: storeId,
id: crypto.randomUUID(),
file: fileInput.files[0],
alt: { en: "Linen shirt on a wooden table" },
});
const imported = await sdk.media.create({
store_id: storeId,
id: crypto.randomUUID(),
source_url: "https://example.com/product-hero.jpg",
});Parameters
| Name | Type | Description |
|---|---|---|
id required | string | The media id the app picks (UUID v4); retry with the same id |
file optional | File | One uploaded file; not with source_url |
source_url optional | string | A public URL Arky downloads; not with file |
alt optional | Record<string, string> | null | Alt text per store language |
A retry with the same id and the same bytes returns the existing media; another file under that id
is refused. Uploads count against the plan’s media limit, which counts files, not bytes.
- A
source_urlmust be anhttpsaddress that stays public at every hop, with at most three redirects, within the size limit. - The record commits only after every stored file is proven by its length and SHA-256. An image’s smaller versions are WebP within their bounding boxes.
Read, Find, Update, Replace
/v1/stores/{store_id}/media/{id} sdk.media.get() /v1/stores/{store_id}/media sdk.media.find() /v1/stores/{store_id}/media/{id} sdk.media.update() /v1/stores/{store_id}/media/{id}/content sdk.media.replaceContent() const page = await sdk.media.find({
store_id: storeId,
type: "image",
sort_field: "created_at",
sort_direction: "desc",
limit: 50,
});
await sdk.media.update({
store_id: storeId,
id: media.id,
expected_updated_at: media.updated_at,
alt: { en: "Linen shirt", de: "Leinenhemd" },
});
await sdk.media.replaceContent({
store_id: storeId,
id: media.id,
expected_updated_at: media.updated_at,
file: newFile,
});
Replacing the file keeps the id and its URLs, so everything that points at the media, sent emails
and built pages included, shows the new file. The new files go into a new generation_id, whose
?v= token is part of each URL, and the old files are deleted after the change commits. Replacing
with the same bytes changes nothing.
A public read streams the stored file itself, never a storage redirect, and answers
Cache-Control: public, max-age=31536000, immutable when ?v= names the current generation and
public, max-age=60 otherwise, so an old link shows a replacement within a minute.
Delete
/v1/stores/{store_id}/media/{id}?expected_updated_at= sdk.media.delete() Deleting is refused while any record points at the file: a block, an email template body
({{media "<id>"}}) or a broadcast body. Otherwise the media becomes deleting, its files are
removed from storage, and then the record goes. Webhooks post media.created, media.updated and
media.deleted.
Storefront
import { getImageUrl } from "arky-sdk/storefront";
const {
items: [hero],
} = await arky.media.find({ ids: [heroMediaId] });
const url = getImageUrl(hero, "large");
const alt = hero.alt?.en ?? "";
getImageUrl returns the named smaller version of an image and the original file of a video or a
PDF.
Files customers send with a form aren’t media. They are stored privately with the form submission; see Forms.