Documentation
Learn how to transform images with simple URL parameters. Our pipeline-based system processes transformations in the order you specify.
Get started in minutes
Sign up at xform.media to get started with your free trial.
Connect your S3 bucket, Cloudflare R2 storage, or any public URL as an image source.
Your images are served from {subdomain}.xform.media
Add query parameters to any image URL to transform on the fly.
Your first transformation
Resize to 800x600, crop to cover, convert to WebP at 85% quality:
https://my-images.xform.media/photos/hero.jpg?w=800&h=600&fit=cover&f=webp&q=85Pipeline-based transformations
xform processes query parameters in the order they appear in the URL, like a pipeline. Each transformation is applied sequentially, allowing you to chain operations in a predictable way.
Parameter Grouping
Some related parameters are automatically grouped together into single operations:
- Resize parameters (
w,h,fit,p) are combined into one resize operation - Modulate parameters (
brightness,saturation,hue) are combined into one color adjustment
When a non-grouped parameter is encountered, any pending grouped operations are executed first.
Interactive Pipeline Demo
Click parameters to add them to the URL. Watch how they get grouped and ordered in the pipeline.
Example: Multi-step pipeline
https://my-images.xform.media/image.jpg?w=800&blur=3&rotate=90&grayscale=true&f=webpExecution order:
- Resize to 800px wide
- Apply blur (sigma=3)
- Rotate 90 degrees
- Convert to grayscale
- Output as WebP
Example: Extract then transform
https://my-images.xform.media/image.jpg?e=100,200,400,300&w=200&blur=2Execution order:
- Extract 400x300 region starting at (100,200)
- Resize to 200px wide
- Apply blur
Transformation parameters
Resize & Crop
| Parameter | Alias | Example | Description |
|---|---|---|---|
width | w | w=400 | Width in pixels (1-8192) |
height | h | h=300 | Height in pixels (1-8192) |
fit | - | fit=cover | cover, contain, fill, inside, outside |
position | p | p=top-left | Crop anchor point |
extract | e | e=10,20,300,400 | Pixel-precise crop (left,top,width,height) |
Rotation & Flip
| Parameter | Alias | Example | Description |
|---|---|---|---|
rotate | r | r=90 | Rotate 0, 90, 180, or 270 degrees |
flip | - | flip=true | Mirror vertically |
flop | - | flop=true | Mirror horizontally |
Effects & Filters
| Parameter | Alias | Example | Description |
|---|---|---|---|
blur | b | b=5 | Gaussian blur (sigma 0.3-1000) |
sharpen | s | s=true | Sharpen image |
brightness | - | brightness=1.2 | Brightness multiplier (1.0 = no change) |
saturation | - | saturation=1.5 | Saturation multiplier (1.0 = no change) |
hue | - | hue=45 | Hue shift (0-360 degrees) |
grayscale | - | grayscale=true | Convert to grayscale |
Format & Quality
| Parameter | Alias | Example | Description |
|---|---|---|---|
format | f | f=webp | jpeg, png, webp, avif, gif, tiff |
quality | q | q=80 | Compression quality (1-100, default: 80) |
Metadata Extraction
| Parameter | Alias | Example | Description |
|---|---|---|---|
metadata | - | ?metadata | Returns image dimensions, format, channels |
colors | - | ?colors | Returns dominant + vibrant color palette |
base64 | - | ?base64 | Returns base64-encoded thumbnail (LQIP) |
exif | - | ?exif | Returns camera/GPS EXIF data (if enabled) |
meta | - | ?meta=true | Returns all metadata combined |
Position values
Basic Positions
toprightbottomleftcenterCorner Positions
top-lefttop-rightbottom-leftbottom-rightCardinal Positions
northsoutheastwestnortheastnorthwestsoutheastsouthwestSmart Positioning
entropyFocus on the busiest area of the image (high detail regions).
attentionFocus on prominent features like faces or objects.
Note: You can use hyphens or underscores interchangeably. For example, p=top-left and p=top_left are equivalent.
Fit values
coverFill dimensions, crop excess (default)
containFit within dimensions, may letterbox
fillStretch to fill (distorts aspect ratio)
insideResize to fit inside dimensions
outsideResize to cover dimensions minimum
OG Image Generation
xform can automatically generate Open Graph (OG) images for your content. These images appear when your links are shared on social media platforms like Twitter, Facebook, LinkedIn, and Slack.
OG images are generated using your source image as the background, with customizable overlays for logos, titles, and descriptions. All styling is configured in the admin dashboard.
Use any image from your source as the background, or upload a custom background image.
Add titles and descriptions with customizable fonts, colors, and positioning.
Add your logo and apply consistent styling across all generated OG images.
Configuring OG Images
Background Options
Uses the requested image as the background (default behavior).
Upload a static background image to use for all OG images.
Use a solid background color instead of an image.
Mask Overlay
Apply a semi-transparent overlay to improve text readability over complex backgrounds.
ColorOpacityLogo Settings
- Upload logo - PNG or SVG with transparency recommended
- Position - Top-left, top-right, bottom-left, bottom-right
- Size - Width and height in pixels
Text Styling
- Font family - Choose from system fonts
- Font size - Title and description sizes
- Text color - Customize for readability
- Position - X/Y coordinates for precise placement
Border
Optionally add a colored border around the OG image for brand recognition.
Using OG Images
OG Image Parameters
| Parameter | Alias | Example | Description |
|---|---|---|---|
type | - | type=og | Enables OG image generation mode |
title | - | title=My%20Title | Main title text (URL-encoded) |
description | - | description=My%20desc | Description text (URL-encoded) |
format | f | f=png | Output format: png, jpeg, or webp (default: png) |
Basic Example
Generate an OG image with title and description:
https://my-images.xform.media/photos/hero.jpg?type=og&title=Welcome%20to%20Our%20Site&description=Discover%20amazing%20contentWithout Source Image
Use the /og endpoint to generate OG images without specifying a source image. Uses your configured default background.
https://my-images.xform.media/og?title=My%20Title&description=My%20descriptionOutput Formats
OG images default to PNG for best quality, but you can specify other formats.
https://my-images.xform.media/image.jpg?type=og&title=Hello&f=webpHTML Meta Tags
Add these meta tags to your HTML to enable social sharing previews:
<meta property="og:image" content="https://{domain}/image.jpg?type=og&title=..." />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://{domain}/image.jpg?type=og&title=..." />Tip: OG images are generated at 1200x630 pixels by default, which is the recommended size for most social platforms.
Adaptive video streaming
xform handles the entire video pipeline: ingest from your existing source (S3, R2, or any supported provider), transcode into multiple quality levels using FFmpeg, and serve via HLS with an embeddable player. Your source files are never modified - xform stores all transcoded output on its own infrastructure.
How it works
Ingest a video from your source, or upload directly to xform via the SDK.
xform transcodes to HLS (1080p/720p/480p) + MP4 + thumbnail automatically.
Embed the player or use HLS URLs directly. Webhooks notify you when ready.
SDK installation
Install
npm install @xformmedia/sdkConfigure the client
import { XformClient } from '@xformmedia/sdk'
const client = new XformClient({
sourceId: 'src_...',
organizationId: 'org_...',
apiKey: 'xfm_...',
baseUrl: 'https://admin.xform.media',
streamBaseUrl: 'https://acme.xform.media'
})Note: The streamBaseUrl is your source's subdomain URL. It's required for generating embed codes and streaming URLs.
Ingest and transcode
The video already lives in your S3, R2, or other configured source. Tell xform the file key and it pulls the video for transcoding. Your source file is never modified.
Don't need the source file in your own storage? Upload directly to xform's infrastructure via a presigned URL. Useful for user-generated content or when you just want the streaming output.
Option A: Ingest from your source
// Ingest a video that already exists in your source
const result = await client.ingest({
key: 'recordings/standup.mp4',
callbackUrl: 'https://example.com/webhook'
})
console.log(result.videoKey)
// → "recordings-standup"
console.log(result.transcodeStatus)
// → "pending"Option B: Upload to xform, then ingest
// Get a presigned URL (uploads to xform, not your source)
const { uploadUrl, key } = await client.createUploadUrl({
filename: 'intro.mp4',
contentType: 'video/mp4'
})
// Upload directly from the browser to xform
await fetch(uploadUrl, {
method: 'PUT',
body: videoFile,
headers: { 'Content-Type': 'video/mp4' }
})
// Then trigger transcoding
const result = await client.ingest({ key })Transcoding details
| Quality | Bitrate | Codec | Notes |
|---|---|---|---|
1080p | 4 Mbps | H.264 | Only if source is 1080p+ |
720p | 2 Mbps | H.264 | Only if source is 720p+ |
480p | 800 kbps | H.264 | Minimum rendition |
Real-time status events
// Listen for transcode status changes via SSE
const stream = client.events()
stream.on('video.ready', (video) => {
console.log('Stream URL:', video.streamUrl)
console.log('Duration:', video.duration, 'seconds')
})
stream.on('video.failed', (video) => {
console.error('Transcode failed:', video.errorMessage)
})
// Clean up when done
stream.close()Embed player
Generate embed code
const iframe = client.embed('intro', {
autoplay: false,
muted: false,
controls: true,
quality: 'auto',
captions: true,
search: true,
color: '6366f1',
title: 'Product Introduction'
})Or use the URL directly
https://acme.xform.media/_embed/intro
?autoplay=1
&muted=1
&quality=720p
&captions=1
&color=6366f1Embed options
autoplayStart playing automatically (requires muted on most browsers).
qualityInitial quality: auto, 1080p, 720p, or 480p.
captionsShow captions by default (if VTT file is available).
searchEnable fuzzy caption search within the player.
tStart playback at a specific time in seconds.
colorAccent color for the player controls (hex without #).
Player features
Webhooks
Webhook events
video.readyTranscoding completed. Includes stream URL, duration, dimensions, and thumbnail URL.
video.failedTranscoding failed after all retry attempts. Includes error message.
video.renamedVideo display name was changed.
Signature verification: All webhooks include an X-Webhook-Signature header containing an HMAC-SHA256 signature for payload verification.
Example payload
{
"event": "video.ready",
"sourceId": "src_...",
"videoKey": "intro",
"timestamp": "2026-03-16T10:30:00Z",
"data": {
"streamUrl": "/_v/intro.m3u8",
"downloadUrl": "/_v/intro.mp4",
"thumbnailUrl": "/_v/intro/thumbnail.jpg",
"duration": 124.5,
"width": 1920,
"height": 1080
}
}Delivery
- 3 retry attempts with exponential backoff
- Per-video callback URL (set on ingest)
- Source-level webhook (configured in admin)
Video analytics
Tracked metrics
Total views, unique sessions, views by day.
Average watch duration, completion rate, play/pause/ended events.
Quality distribution across viewers, quality change events.
Top referring domains and pages.
Query analytics via SDK
// Summary analytics
const summary = await client.videos.analytics()
console.log(summary.totalViews)
console.log(summary.avgCompletionRate)
// Detailed breakdown for a specific video
const detailed = await client.videos.analytics({
videoKey: 'intro',
detailed: true,
since: '2026-03-01',
until: '2026-03-16'
})Video management
// List all videos
const { data } = await client.videos.list()
// Get a specific video
const video = await client.videos.get('intro')
// Upload captions
await client.videos.uploadCaptions(
'intro',
'WEBVTT\n\n00:00.000 --> 00:05.000\nHello!'
)
// Delete a video
await client.videos.delete(video.id)Source configuration
Supported Storage Providers
Standard S3 buckets with IAM credentials.
S3-compatible storage with zero egress fees.
Any publicly accessible web URL as an image source.
Required Credentials
- Bucket name - Your storage bucket identifier
- Region - AWS region or R2 account ID
- Access Key ID - IAM or R2 API token
- Secret Access Key - Associated secret
Security tip: Only provide credentials with read-only permissions. xform only needs to read your images, never write or delete.
Your Access Key ID and Secret Access Key are encrypted at rest and never exposed in logs or API responses.
Optional Settings
- Cache settings - Configure max-age and s-maxage for CDN
- EXIF extraction - Enable/disable EXIF metadata extraction
Custom domains
Instead of serving images from {subdomain}.xform.media, you can use your own subdomain like cdn.example.com or media.example.com.
Important: Only subdomains are supported (e.g., cdn.example.com). Apex/root domains (e.g., example.com) are not supported.
Setup Steps
Point your subdomain to xform.media in your DNS provider.
cdn.example.com CNAME xform.mediaAdd the TXT record shown in the admin dashboard to prove domain ownership.
_cf-custom-hostname.cdn.example.com TXT <verification-token>Enter your subdomain in the source settings. SSL is provisioned automatically once DNS records are verified.
Example
Once configured, your images are served from your custom subdomain:
Serve transformed images from your own domain:
https://cdn.example.com/photos/hero.jpg?w=800&f=webpApex domains not supported: You must use a subdomain (e.g., cdn.example.com), not a root domain (e.g., example.com). This is because CNAME records cannot be set on apex domains with most DNS providers.
DNS propagation: It may take a few minutes for DNS changes to propagate. The admin dashboard will show the verification status in real time.
Caching
xform automatically caches transformed images using CDN edge caching. Cache headers are configured per-source:
Cache-Control: public, max-age=31536000, s-maxage=31536000max-age
Browser cache duration. Configure this based on how often your images change.
s-maxage
CDN cache duration. Set this higher for longer edge caching.
Response types
Image Response (default)
When you request a transformation without metadata parameters, xform returns the transformed image with the appropriate Content-Type header.
Returns a WebP image:
https://my-images.xform.media/photo.jpg?w=400&f=webpJSON Metadata Response
When you include metadata parameters, xform returns a JSON response with the requested data.
Returns JSON with dimensions and color palette:
https://my-images.xform.media/photo.jpg?metadata&colorsExample JSON Response
{
"metadata": {
"width": 1920,
"height": 1080,
"format": "jpeg",
"channels": 3,
"hasAlpha": false
},
"colors": {
"dominant": { "rgb": [64, 128, 192], "hex": "#4080c0" },
"vibrant": { "rgb": [48, 160, 255], "hex": "#30a0ff" },
"palette": [...]
}
}Error handling
| Status Code | Meaning |
|---|---|
400 | Invalid parameters - check your query string |
403 | Source suspended - contact support |
404 | Image not found at the specified path |
500 | Processing error - try again or contact support |
Technical constraints
Supported Output Formats
jpegjpgpngwebpavifgiftiff