MagicStack Imaginary

Developer API Reference

1. Overview

MagicStack Imaginary is a lightweight image-hosting and on-the-fly transformation service. You upload an image once and receive a short image code that you embed wherever you need the image. Resized or quality-adjusted variants are generated on first request and cached automatically.

All endpoint paths below are shown relative to the service root.

Authentication: Every upload request must include the X-Tenant-ID header that identifies your application. Image retrieval is unauthenticated so that images can be embedded in any web page or email.

2. Uploading Images

The upload endpoint supports two methods: multipart form upload (standard file upload) and JSON base64 upload (useful for API integrations).

POST /upload

Method 1: Multipart Form Upload

Send a multipart/form-data POST request with your image file to receive a unique image code.

Request Headers

HeaderRequiredDescription
X-Tenant-ID Yes Your application's tenant identifier.
Content-Type Yes Must be multipart/form-data (set automatically by most HTTP clients).

Request Body (form fields)

FieldRequiredDescription
file Yes The image file to upload (JPEG, PNG, GIF, WebP, etc.).
folder No Logical grouping for the image (e.g. avatars, products). Defaults to default.

Example – cURL

curl -X POST https://your-domain.com/upload \
  -H "X-Tenant-ID: my-app" \
  -F "file=@/path/to/photo.jpg" \
  -F "folder=products"

Example – JavaScript (fetch)

const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('folder', 'products');

const response = await fetch('/upload', {
  method: 'POST',
  headers: { 'X-Tenant-ID': 'my-app' },
  body: form,
});

const { image_code } = await response.json();
console.log('Uploaded image code:', image_code);

Method 2: JSON Base64 Upload

Send a JSON payload containing a base64-encoded image string. This method is useful for API integrations, mobile apps, or when the image is already in memory as base64.

Request Headers

HeaderRequiredDescription
X-Tenant-ID Yes Your application's tenant identifier.
Content-Type Yes Must be application/json.

Request Body (JSON fields)

FieldRequiredDescription
file_base64 Yes* Base64-encoded image string (raw or data URL format).
image_base64 Yes* Alternative field name for base64 image string.
base64 Yes* Alternative field name for base64 image string.
file Yes* Alternative field name for base64 image string.
filename No Original filename (used to infer file extension). If omitted, extension is inferred from data URL or defaults to jpg.
folder No Logical grouping for the image. Defaults to default.

* One of the base64 fields is required. The endpoint checks file_base64, image_base64, base64, and file in that order.

Base64 Format

The base64 string can be provided in two formats:

Example – cURL (data URL)

curl -X POST https://your-domain.com/upload \
  -H "X-Tenant-ID: my-app" \
  -H "Content-Type: application/json" \
  -d '{
    "file_base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR4AWP4DwQACfsD/c8LaHIAAAAASUVORK5CYII=",
    "filename": "tiny.png",
    "folder": "avatars"
  }'

Example – JavaScript (fetch)

// Convert file to base64
function fileToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

const base64String = await fileToBase64(fileInput.files[0]);

const response = await fetch('/upload', {
  method: 'POST',
  headers: {
    'X-Tenant-ID': 'my-app',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    file_base64: base64String,
    filename: fileInput.files[0].name,
    folder: 'products',
  }),
});

const { image_code } = await response.json();
console.log('Uploaded image code:', image_code);

Response

Both methods return HTTP 201 Created with a JSON body containing the new image code:

{
  "image_code": "abc123XYZ789"
}
Upload requests are rate-limited to 30 requests per minute per IP address. For large base64 uploads (>5MB), ensure the JSON_BODY_LIMIT environment variable is configured appropriately (default: 15mb).

3. Linking to Images

Once you have an image code you can reference the original image directly in an <img> tag or any URL.

GET /:imageCode

Path Parameters

ParameterDescription
imageCode The 12-character code returned by the upload endpoint.

Response

The original image file is streamed back with the correct Content-Type header (e.g. image/jpeg, image/png). If the image code is not found, a placeholder JPEG image is returned with HTTP 404.

HTML Example

<img src="/abc123XYZ789" alt="Product photo" />

Markdown Example

![Product photo](/abc123XYZ789)
Image serving is rate-limited to 200 requests per minute per IP address.

4. Resizing & Transforming Images in Links

Append a transform segment before the image code to receive a resized and/or quality-adjusted JPEG variant. Variants are generated on first request and cached for subsequent calls.

GET /:transform/:imageCode

Transform Format

The transform is a comma-separated list of key_value pairs. All parameters are optional but at least one must be present.

ParameterExampleDescription
w_<pixels> w_400 Maximum output width in pixels.
h_<pixels> h_300 Maximum output height in pixels.
q_<1-100> q_75 JPEG quality (1 = lowest, 100 = highest). Defaults to 50.
Aspect ratio is always preserved. Images are never enlarged beyond their original dimensions. The output format is always JPEG regardless of the original format.

Examples

URLEffect
/w_400/abc123XYZ789 Resize to max width 400 px, quality 50 (default)
/h_300/abc123XYZ789 Resize to max height 300 px, quality 50 (default)
/w_800,h_600/abc123XYZ789 Resize to fit within 800 × 600 px
/w_400,q_75/abc123XYZ789 Resize to max width 400 px at 75% JPEG quality
/q_90/abc123XYZ789 Original dimensions, higher quality (90%)

HTML Example

<!-- Thumbnail: 200 px wide, default quality -->
<img src="/w_200/abc123XYZ789" alt="Thumbnail" />

<!-- Full-width hero: max 1200 px wide, quality 80 -->
<img src="/w_1200,q_80/abc123XYZ789" alt="Hero" />

Responsive Images Example

<img
  src="/w_800/abc123XYZ789"
  srcset="
    /w_400/abc123XYZ789  400w,
    /w_800/abc123XYZ789  800w,
    /w_1200/abc123XYZ789 1200w
  "
  sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
  alt="Responsive product image"
/>

5. Automatic Image Cleanup

To keep storage usage under control, the service automatically removes cached transform variants that have not been accessed recently.

What gets cleaned up

Only variant files (the resized/transformed copies) are eligible for cleanup. The original uploaded image is never deleted automatically.

Cleanup schedule

SettingDefaultDescription
Stale threshold 7 days A cached variant is removed if its last_accessed timestamp is older than 7 days.
Cleanup interval Every hour The cleanup job runs periodically. Override with the CLEANUP_INTERVAL_MS environment variable (value in milliseconds).

What happens after cleanup

If a client requests a variant that was previously cleaned up, the service regenerates it on-the-fly from the original image and caches it again – so no data is permanently lost.

Note: Original images are stored indefinitely. If you need to remove an original image, contact your system administrator.

6. Error Responses

All errors use a consistent JSON envelope:

{
  "error": {
    "message": "Human-readable description of the error"
  }
}

Common HTTP status codes

CodeMeaning
400Bad request – missing or invalid parameters (e.g. no file, missing X-Tenant-ID, invalid transform string).
404Image not found – a placeholder JPEG is returned instead of an error body.
429Too many requests – rate limit exceeded. Wait before retrying.
500Internal server error – unexpected failure on the server side.