Back to arky.io

Media

File uploads, image management, and asset handling

The Media module handles file uploads, image processing, and asset management.

Upload Media

POST /v1/businesses/{businessId}/media
SDK: sdk.media.uploadBusinessMedia()

Upload files to your media library. Supports uploading multiple files and/or URLs.

// Upload from file input (multiple files)
const fileInput = document.querySelector('input[type="file"]');
const files = Array.from(fileInput.files);

const result = await sdk.media.uploadBusinessMedia({
files: files,  // Array of File objects
urls: []       // Can also provide URLs to download
});

result.forEach(media => {
console.log('Uploaded:', media.url);
console.log('Media ID:', media.id);
});

// Upload from URLs
const urlResult = await sdk.media.uploadBusinessMedia({
files: [],
urls: [
  'https://example.com/image1.jpg',
  'https://example.com/image2.png'
]
});

Parameters

Name Type Description
files optional File[] Array of files to upload (multipart field name: files)
urls optional string[] Array of URLs to download and store (multipart field name: urls)
Note

At least one of files or urls must be provided with at least one item.

Supported File Types

CategoryExtensions
Images.jpg, .jpeg, .png, .gif, .webp, .svg, .avif
Documents.pdf, .doc, .docx, .xls, .xlsx
Video.mp4, .webm, .mov
Audio.mp3, .wav, .ogg

Size Limits

  • Images: 10MB
  • Documents: 25MB
  • Video: 100MB
  • Audio: 50MB

Get Single Media Item

GET /v1/businesses/{businessId}/media/{mediaId}
SDK: sdk.media.getMedia()

Retrieve a single media item by ID.

const media = await sdk.media.getMedia({
  mediaId: 'media_xyz789'
});

console.log(media.url, media.mimeType, media.size);

Parameters

Name Type Description
mediaId required string Media ID to retrieve
businessId optional string Business ID (defaults to the SDK config)

List Media

GET /v1/businesses/{businessId}/media
SDK: sdk.media.getBusinessMedia()

List all media files.

const result = await sdk.media.getBusinessMedia({
mimeType: 'image/jpeg',
query: 'product hero',
limit: 50,
sortField: 'uploadedAt',
sortDirection: 'desc'
});

result.items.forEach(media => {
console.log(media.id, media.url, media.size);
});

Parameters

Name Type Description
ids optional string[] Filter by specific media IDs
mimeType optional string Filter by MIME type (image/jpeg, image/png, video/mp4, etc.)
query optional string Search in filename and metadata
sortField optional string Sort field
sortDirection optional asc | desc Sort direction
cursor optional string Pagination cursor
limit required number Items per page

Update Media

PUT /v1/businesses/{businessId}/media/{mediaId}
SDK: sdk.media.updateMedia()

Update media metadata. Currently supports localized slug updates used to generate CDN-friendly filenames.

const result = await sdk.media.updateMedia({
  mediaId: 'media_xyz789',
  slug: {
    en: 'product-hero-front-view',
    es: 'producto-hero-vista-frontal'
  }
});

Parameters

Name Type Description
mediaId required string Media ID to update
slug optional Record<string, string> Localized slug for the media filename

Delete Media

DELETE /v1/businesses/{id}/media/{mediaId}
SDK: sdk.media.deleteBusinessMedia()

Delete a media file.

Warning

Deleting media that’s in use (products, nodes, etc.) may cause broken images. Check references before deleting.

const result = await sdk.media.deleteBusinessMedia({
  id: 'biz_abc123',
  mediaId: 'media_xyz789'
});

Parameters

Name Type Description
id required string Business ID
mediaId required string Media ID to delete

Image Transformations

Arky provides on-the-fly image transformations via URL parameters.

Resize

// Original URL
const originalUrl = media.url;
// https://cdn.arky.io/media/biz_abc123/image.jpg

// Resize to width 400px (height auto)
const resized = `${originalUrl}?w=400`;

// Resize to exact dimensions
const exact = `${originalUrl}?w=400&h=300`;

// Fit within bounds (maintain aspect ratio)
const fit = `${originalUrl}?w=400&h=300&fit=contain`;

// Cover (crop to fill)
const cover = `${originalUrl}?w=400&h=300&fit=cover`;

Quality

// Reduce quality for smaller file size
const compressed = `${originalUrl}?q=75`;

Format Conversion

// Convert to WebP
const webp = `${originalUrl}?format=webp`;

// Convert to AVIF
const avif = `${originalUrl}?format=avif`;

Transformation Parameters

ParameterDescriptionExample
wWidth in pixels?w=400
hHeight in pixels?h=300
fitResize modecontain, cover, fill
qQuality (1-100)?q=80
formatOutput formatwebp, avif, jpg, png
blurBlur amount (1-100)?blur=10
grayscaleConvert to grayscale?grayscale=true

Responsive Images

Generate srcset for responsive images:

function getResponsiveSrcSet(media: Media) {
  const baseUrl = media.url;
  const widths = [320, 640, 960, 1280, 1920];

  return widths
    .map(w => `${baseUrl}?w=${w}&format=webp ${w}w`)
    .join(', ');
}

// Usage in HTML
const srcSet = getResponsiveSrcSet(productImage);
// <img srcset={srcSet} sizes="(max-width: 768px) 100vw, 50vw" />

Upload with Progress

Track upload progress for large files:

async function uploadWithProgress(
  files: File[],
  onProgress: (percent: number) => void
) {
  const formData = new FormData();
  files.forEach((file, i) => {
    formData.append(`files[${i}]`, file);
  });

  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    xhr.upload.addEventListener('progress', (e) => {
      if (e.lengthComputable) {
        const percent = Math.round((e.loaded / e.total) * 100);
        onProgress(percent);
      }
    });

    xhr.addEventListener('load', () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(new Error('Upload failed'));
      }
    });

    xhr.open('POST', `https://api.arky.io/v1/businesses/${businessId}/media`);
    xhr.setRequestHeader('Authorization', `Bearer ${token}`);
    xhr.send(formData);
  });
}

// Usage
await uploadWithProgress(files, (percent) => {
  console.log(`Upload progress: ${percent}%`);
  progressBar.style.width = `${percent}%`;
});

Bulk Upload

Upload multiple files using the SDK:

async function uploadMultiple(files: File[]) {
  // The SDK handles batching automatically
  const results = await sdk.media.uploadBusinessMedia({
    files: files
  });

  console.log(`Uploaded ${results.length} files`);
  return results;
}

// Usage
const fileInput = document.querySelector('input[type="file"][multiple]');
const uploaded = await uploadMultiple([...fileInput.files]);

Referencing Media from Content

Reference media by ID via a RELATIONSHIP_MEDIA block in nodes, products, services, or providers:

// Upload images first
const uploadResult = await sdk.media.uploadBusinessMedia({
  files: [heroFile, ...galleryFiles]
});

const [heroMedia, ...galleryMedia] = uploadResult;

// Create a node referencing the media
await sdk.cms.createNode({
  key: 'my-product-page',
  blocks: [
    {
      key: 'hero-image',
      type: 'RELATIONSHIP_MEDIA',
      properties: {},
      value: [`media:${heroMedia.id}`]
    },
    {
      key: 'gallery',
      type: 'RELATIONSHIP_MEDIA',
      properties: {},
      value: galleryMedia.map(m => `media:${m.id}`)
    }
  ]
});
Tip

Use the slug field on media to produce clean, SEO-friendly filenames in CDN URLs.