Create key
Dashboard Settings -> API keys. Copy the full `ik_live_...` key immediately.
Developer docs
Use scoped `ik_live_...` keys to upload, import, search, transform, sign, and organize image assets stored in Cloudflare R2 through ImgPanel.
curl -X GET "https://api.imgpanel.com/v1/control/r2/assets?limit=20" \
-H "x-api-key: $IMGPANEL_API_KEY"Create an API key in Settings, then call the runtime API.
Dashboard Settings -> API keys. Copy the full `ik_live_...` key immediately.
Store it as `IMGPANEL_API_KEY` in your server, job runner, or CI secret store.
Send the key as `x-api-key` or `Authorization: Bearer ik_live_...`.
Import the ImgPanel runtime API into Postman, Insomnia, or SDK generators.
curl "https://api.imgpanel.com/v1/openapi.json"Download specDashboard sessions are for the UI. Runtime integrations use scoped ImgPanel API keys. Cloudflare tokens stay server-side and are never returned to frontend code.
curl "https://api.imgpanel.com/v1/control/r2/assets?limit=20" \
-H "x-api-key: $IMGPANEL_API_KEY"API keys cannot manage other API keys, Cloudflare account connections, or dashboard-only playground endpoints. Those actions require a logged-in dashboard session.
Embed uploads in a CMS or admin UI without exposing ImgPanel API keys.
1. Server creates a token
curl -X POST "https://api.imgpanel.com/v1/control/r2/upload-tokens" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
-d '{
"clientUploadId": "cms-media-field-42",
"expiresInSeconds": 900,
"folder": "direct/inbox",
"tags": ["direct-upload"],
"metadata": { "source": "browser-direct-upload" }
}'2. Browser posts the file
async function uploadImage(file, tokenResponse) {
const form = new FormData();
form.set("file", file);
form.set("token", tokenResponse.token);
const response = await fetch(tokenResponse.upload_url, {
method: "POST",
body: form
});
if (!response.ok) {
throw new Error(await response.text());
}
return response.json();
}Create tokens from your backend with an ImgPanel API key that has `uploads:write`; never create them directly from browser JavaScript.
Send `multipart/form-data` to the returned `upload_url` with fields `file` and `token`, or pass the token as `Authorization: Bearer ...`.
`expiresInSeconds` defaults to 900 seconds, accepts 60 to 3600 seconds, and returns `expires_at` so your UI can expire stale forms.
Browser CORS for `/r2/direct-upload` is controlled by `IMGPANEL_DIRECT_UPLOAD_ORIGINS`; list exact HTTPS origins for embedded CMS/admin sites.
Folder, tags, metadata, status, access, and upload preset rules are baked into the token. Browser-side overrides work only when the token was created with `allowOverrides`.
Tokens are upload-only, team-scoped, signed, and short-lived. Keep API keys server-side and treat generated upload tokens as disposable form credentials.
Keep a clear mental model for current originals, version snapshots, and safe restore workflows.
# 1. Read the Cloudinary-style resource detail.
curl "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/detail?public_id=legacy/home/hero" \
-H "x-api-key: $IMGPANEL_API_KEY"
# 2. Download the current original/base from the returned source_url.
curl -L "$SOURCE_URL" -o hero-original.jpg# Restore an archived resource by public_id.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/restore" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
-d '{ "public_ids": ["legacy/home/hero"] }'The asset detail `source_url` and dashboard Download button target the current original/base file stored at the asset key. Derived assets are separate generated files.
Snapshots are private recovery copies of a previous base file. Use the dashboard Versions panel to compare filename, type, and size before restoring one.
Restoring a snapshot replaces the current base file bytes and metadata while keeping the same asset key. Existing public delivery URLs keep working, but they now serve the restored file after cache refresh.
Archive/restore changes lifecycle visibility without changing the object bytes. Version restore is the recovery path for rolling back a replacement.
Download the current original before destructive edits, create a labeled snapshot, then replace or transform. Keep Cloudinary online until migrated public IDs and signed delivery flows are verified.
These examples are generated from the same workflow catalog used by the dashboard API Explorer.
Fetch indexed R2 assets with pagination, lifecycle counts, and Library facet counts for folders, tags, access, status, format, creators, and presets.
curl -X GET "https://api.imgpanel.com/v1/control/r2/assets?limit=20" \
-H "x-api-key: $IMGPANEL_API_KEY"Cloudinary-style expression search over the R2 index, including AND, OR, NOT, parentheses, metadata fields, and numeric/date comparisons.
curl -X POST "https://api.imgpanel.com/v1/control/r2/search" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"expression": "(tags=hero OR tags=product) AND NOT status=draft",
"max_results": 20
}'Import an image URL into R2 with tags, metadata, and optional Cloudinary-like public_id conflict policy. Set overwrite=true to replace an existing public_id.
curl -X POST "https://api.imgpanel.com/v1/control/r2/assets" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"metadata": {
"source": "api-explorer"
},
"overwrite": false,
"public_id": "imports/flower-demo",
"tags": [
"api"
],
"url": "https://www.imgpanel.com/images/transform-sample.png"
}'Import selected website grab image candidates into R2. Grab imports derive stable public IDs from source paths, so overwrite and unique_filename control repeat-import conflicts.
curl -X POST "https://api.imgpanel.com/v1/control/r2/grab/import" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"images": [
{
"source_page_url": "https://example.com/products",
"url": "https://example.com/images/hero.jpg"
}
],
"overwrite": false,
"presetId": "upr_example",
"sourceSiteUrl": "https://example.com",
"unique_filename": false
}'Bulk edit selected R2 assets: folder, access, status, tags, and metadata.
curl -X PATCH "https://api.imgpanel.com/v1/control/r2/assets/bulk" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"folder": "campaigns/summer",
"keys": [
"uploads/tm_example/2026/07/04/image.jpg"
],
"metadata": {
"campaign": "summer"
},
"metadataMode": "merge",
"status": "approved",
"tagMode": "add",
"tags": [
"hero",
"approved"
]
}'Permanently delete selected R2 assets. Archive active assets first from the dashboard when you need a reversible delete.
curl -X POST "https://api.imgpanel.com/v1/control/r2/assets/bulk-delete" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"keys": [
"uploads/tm_example/2026/07/04/image.jpg"
]
}'Generate and store an eager/derived transformed asset from an existing R2 source.
curl -X POST "https://api.imgpanel.com/v1/control/r2/derived" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"options": {
"format": "webp",
"quality": 85,
"width": 1200
},
"source_key": "uploads/tm_example/2026/07/04/image.jpg"
}'Create an async derived-generation job for selected R2 sources. Run it with the job run endpoint until completed.
curl -X POST "https://api.imgpanel.com/v1/control/r2/derived/jobs" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"keys": [
"uploads/tm_example/2026/07/04/hero.jpg",
"uploads/tm_example/2026/07/04/card.jpg"
],
"options": {
"fit": "scale-down",
"format": "webp",
"quality": 85,
"width": 1200
}
}'Process the next parallel step for a derived-generation job and return progress, created count, failed count, and item statuses.
curl -X POST "https://api.imgpanel.com/v1/control/r2/derived/jobs/djob_example/run" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{}'Requeue failed items from a derived-generation job, then run the same job again to retry only failed sources.
curl -X POST "https://api.imgpanel.com/v1/control/r2/derived/jobs/djob_example/retry-failed" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{}'Read recent logs for a derived-generation job, including run steps, completed items, failed items, retry events, and cancellation.
curl -X GET "https://api.imgpanel.com/v1/control/r2/derived/jobs/djob_example/logs?limit=20" \
-H "x-api-key: $IMGPANEL_API_KEY"Check that a generated delivery or Open Graph image URL is reachable and image-like, including cache-control, CDN cache status, age, ETag, and redirect diagnostics.
curl -X GET "https://api.imgpanel.com/v1/control/delivery/inspect?url=https%3A%2F%2Fwww.imgpanel.com%2Fcdn-cgi%2Fimage%2Ffit%3Dcover%2Cformat%3Dauto%2Cwidth%3D1200%2Fimages%2Ftransform-sample.png" \
-H "x-api-key: $IMGPANEL_API_KEY"Invalidate CDN cache for selected ImgPanel delivery URLs. Pass connection_id to use a client delivery connection, or omit it for the workspace default/platform fallback.
curl -X POST "https://api.imgpanel.com/v1/control/delivery/purge" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"urls": [
"https://api.imgpanel.com/r2/uploads/tm_example/2026/07/04/hero.jpg",
"https://www.imgpanel.com/cdn-cgi/image/fit=cover,format=auto,width=1200/images/transform-sample.png"
]
}'List reusable delivery profiles for responsive and social image snippets.
curl -X GET "https://api.imgpanel.com/v1/control/delivery/profiles" \
-H "x-api-key: $IMGPANEL_API_KEY"Create a named delivery profile that can be reused from the asset details UI.
curl -X POST "https://api.imgpanel.com/v1/control/delivery/profiles" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"name": "Social hero",
"settings": {
"quality": "85",
"sizes": "(max-width: 768px) 100vw, 768px",
"socialImageHeight": "630",
"socialImageWidth": "1200",
"socialSiteName": "ImgPanel",
"widths": "320, 640, 960, 1280, 1600"
}
}'Create a short-lived browser direct upload token. Call this from your server with an API key, then give only the returned token and upload_url to browser code.
curl -X POST "https://api.imgpanel.com/v1/control/r2/upload-tokens" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"clientUploadId": "cms-media-field-42",
"expiresInSeconds": 900,
"folder": "direct/inbox",
"metadata": {
"source": "browser-direct-upload"
},
"tags": [
"direct-upload"
]
}'Create a short-lived public asset picker token for embedding ImgPanel media search into a CMS or admin UI without exposing a runtime API key.
curl -X POST "https://api.imgpanel.com/v1/control/r2/asset-picker-tokens" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"expiresInSeconds": 900,
"requestId": "cms-media-field-42",
"targetOrigin": "https://cms.example.com"
}'List public R2 assets through a short-lived asset picker token. This endpoint is intended for iframe/embed picker flows and does not accept runtime API keys.
curl -X GET "https://api.imgpanel.com/v1/r2/asset-picker?token=PASTE_PICKER_TOKEN&search=hero&tag=cms&limit=20"Read storage, delivery, activity, asset health, and optional Cloudflare GraphQL cache attribution stats.
curl -X GET "https://api.imgpanel.com/v1/control/r2/stats?sinceDays=30" \
-H "x-api-key: $IMGPANEL_API_KEY"Configure workspace media defaults: upload MIME/size limits, default access/status/folder, and the delivery base URL stored on newly uploaded R2 assets.
curl -X PATCH "https://api.imgpanel.com/v1/control/r2/settings" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"allowed_mime_types": [
"image/jpeg",
"image/png",
"image/webp"
],
"default_access": "private",
"default_folder": "incoming",
"default_status": "review",
"delivery_base_url": "https://cdn.example.com/media",
"max_upload_bytes": 10485760
}'List structured metadata fields, including conditional field rules used by Library forms and upload validation.
curl -X GET "https://api.imgpanel.com/v1/control/r2/metadata-fields" \
-H "x-api-key: $IMGPANEL_API_KEY"Create a structured metadata field. Conditions make a field active only when other metadata values match.
curl -X POST "https://api.imgpanel.com/v1/control/r2/metadata-fields" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"conditions": [
{
"field": "asset_kind",
"operator": "equals",
"value": "photo"
}
],
"key": "model_release",
"label": "Model release",
"required": true,
"sortOrder": 20,
"type": "url"
}'Update a structured metadata field and its conditional visibility/validation rules.
curl -X PATCH "https://api.imgpanel.com/v1/control/r2/metadata-fields/mf_example" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"conditions": [
{
"field": "asset_kind",
"operator": "in",
"values": [
"photo",
"portrait"
]
}
],
"key": "model_release",
"label": "Model release",
"required": true,
"sortOrder": 20,
"type": "url"
}'List upload presets used by browser uploads, URL imports, direct uploads, and website grab imports.
curl -X GET "https://api.imgpanel.com/v1/control/presets" \
-H "x-api-key: $IMGPANEL_API_KEY"Create a reusable upload preset with folder/tags/metadata defaults, allowed formats, max size, moderation defaults, and optional eager transform JSON.
curl -X POST "https://api.imgpanel.com/v1/control/presets" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"access": "private",
"allowedFormats": [
"jpg",
"png",
"webp"
],
"folder": "products/incoming",
"maxFileSize": 10485760,
"metadata": {
"source": "upload-preset"
},
"name": "Product uploads",
"status": "review",
"tags": [
"product",
"needs-review"
],
"transform": {
"format": "webp",
"quality": 85,
"width": 1600
}
}'Update an upload preset. Future uploads using this preset receive the new defaults and constraints.
curl -X PATCH "https://api.imgpanel.com/v1/control/presets/upr_example" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"access": "public",
"allowedFormats": [
"jpg",
"png",
"webp",
"avif"
],
"folder": "products/approved",
"maxFileSize": 15728640,
"metadata": {
"source": "upload-preset",
"reviewed": true
},
"name": "Product uploads approved",
"status": "approved",
"tags": [
"product",
"approved"
],
"transform": {
"format": "webp",
"quality": 82,
"width": 1800
}
}'Filter and export audit activity for uploads, imports, metadata edits, jobs, and delivery operations.
curl -X GET "https://api.imgpanel.com/v1/control/r2/activity?resource_type=asset&search=uploaded&export=true&limit=1000" \
-H "x-api-key: $IMGPANEL_API_KEY"Export selected asset metadata as JSON and include backup context for folders, tags, collections, recent audit activity, migration jobs, duplicate checksum groups, metadata schema, and review-status counts.
curl -X POST "https://api.imgpanel.com/v1/control/r2/assets/export" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"expires_in_seconds": 3600,
"include_backup": true,
"keys": [
"uploads/tm_example/2026/07/hero.jpg"
],
"signed_urls": true
}'List reusable transformation templates.
curl -X GET "https://api.imgpanel.com/v1/control/transformation-templates" \
-H "x-api-key: $IMGPANEL_API_KEY"List saved versions for a transformation template so an integration can audit or roll back named transform changes.
curl -X GET "https://api.imgpanel.com/v1/control/transformation-templates/trn_example/versions" \
-H "x-api-key: $IMGPANEL_API_KEY"Restore a transformation template from a previous saved version. The restore itself becomes the newest version.
curl -X POST "https://api.imgpanel.com/v1/control/transformation-templates/trn_example/versions/1/restore" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{}'List team collections for asset picker/gallery flows.
curl -X GET "https://api.imgpanel.com/v1/control/r2/collections" \
-H "x-api-key: $IMGPANEL_API_KEY"Save encrypted Cloudinary Admin API credentials for repeatable migration pulls. Responses only return safe hints; api_secret is never returned.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/connections" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"api_key": "1234567890",
"api_secret": "encrypted-and-never-returned",
"cloud_name": "demo",
"default_prefix": "legacy/",
"name": "Production Cloudinary"
}'List saved Cloudinary connections for the workspace. Secrets are redacted.
curl -X GET "https://api.imgpanel.com/v1/control/r2/cloudinary/connections" \
-H "x-api-key: $IMGPANEL_API_KEY"List ImgPanel R2 assets in a Cloudinary Resources API compatible shape. Supports max_results, next_cursor, prefix, tag, public_id, tags=false, context=false, and metadata=false.
curl -X GET "https://api.imgpanel.com/v1/control/r2/cloudinary/resources?max_results=50&prefix=legacy/&tags=true&context=true&metadata=true" \
-H "x-api-key: $IMGPANEL_API_KEY"Search ImgPanel R2 assets and return Cloudinary-style resources. Supports Cloudinary-like expression, tag/folder/metadata filters, cursor pagination, facets, and archive lifecycle counts.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/search" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"expression": "tags=hero AND metadata.campaign=summer",
"max_results": 20,
"sort_by": [
{
"uploaded_at": "desc"
}
]
}'Upload a remote image through a Cloudinary-like endpoint. Accepts multipart file uploads or JSON file/url remote uploads, maps upload_preset to ImgPanel presets, and returns a Cloudinary-style resource.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/upload" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"context": "alt=Homepage hero|caption=Uploaded through Cloudinary-compatible API",
"file": "https://www.imgpanel.com/images/transform-sample.png",
"overwrite": false,
"public_id": "legacy/home/hero",
"tags": "cloudinary,hero",
"unique_filename": false,
"upload_preset": "upr_example"
}'Read one Cloudinary-style resource by public_id, including metadata, derived assets, version snapshots, internal R2 key, and the current original/base source_url for download.
curl -X GET "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/detail?public_id=legacy/home/hero&archived=all" \
-H "x-api-key: $IMGPANEL_API_KEY"Update Cloudinary-style resource context and metadata by public_id. Use metadata_mode=merge or replace.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/update" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"context": {
"alt": "Homepage hero",
"caption": "Imported from Cloudinary"
},
"metadata": {
"campaign": "summer",
"owner": "marketing"
},
"metadata_mode": "merge",
"public_ids": [
"legacy/home/hero"
]
}'Delete Cloudinary-style resources by public_id. mode=archive is the safe default; use mode=delete only for permanent R2 object deletion.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/delete" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"mode": "archive",
"public_ids": [
"legacy/home/hero"
]
}'Restore previously archived Cloudinary-style resources by public_id. This is lifecycle recovery; use the dashboard asset Versions panel when you need to roll the current base file back to a snapshot.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/restore" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"public_ids": [
"legacy/home/hero"
]
}'Rename or move one Cloudinary-style resource by public_id. Updates the R2 object key, folder/filename metadata, and cloudinary_public_id.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/rename" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"overwrite": false,
"public_id": "legacy/home/hero",
"to_public_id": "legacy/home/hero-renamed"
}'List derived/generated resources for one Cloudinary-style public_id.
curl -X GET "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/derived?public_id=legacy/home/hero" \
-H "x-api-key: $IMGPANEL_API_KEY"Generate one derived/transformed resource for a Cloudinary-style public_id using ImgPanel transformation options or a transformation_template_id.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/derived" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"options": {
"format": "webp",
"quality": 80,
"width": 1200
},
"public_id": "legacy/home/hero",
"tags": [
"derived",
"webp"
]
}'Delete one derived/generated resource by derived_id, scoped to a Cloudinary-style public_id.
curl -X DELETE "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/derived?public_id=legacy/home/hero&derived_id=drv_example" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{}'List Cloudinary-style tags from the R2 asset index with counts and cursor pagination.
curl -X GET "https://api.imgpanel.com/v1/control/r2/cloudinary/tags?max_results=100&prefix=hero" \
-H "x-api-key: $IMGPANEL_API_KEY"List Cloudinary-style folders from ImgPanel R2 folder metadata. Pass path to list direct subfolders under a folder.
curl -X GET "https://api.imgpanel.com/v1/control/r2/cloudinary/folders?path=legacy&max_results=100" \
-H "x-api-key: $IMGPANEL_API_KEY"Add one or more tags to resources by Cloudinary public_id. Works for migrated Cloudinary assets and ImgPanel assets with folder/filename public IDs.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/tags/add" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"public_ids": [
"legacy/home/hero"
],
"tag": "approved"
}'Remove one or more tags from resources by Cloudinary public_id.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/tags/remove" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"public_ids": [
"legacy/home/hero"
],
"tags": [
"legacy",
"needs-review"
]
}'Remove all tags from resources by Cloudinary public_id.
curl -X POST "https://api.imgpanel.com/v1/control/r2/cloudinary/tags/remove-all" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"public_ids": [
"legacy/home/hero"
]
}'Create a step-run migration job from a Cloudinary-style asset manifest. Supports secure_url/url, public_id, folder, tags, metadata/context, upload presets, overwrite/unique_filename conflict policy, and retryable item failures.
curl -X POST "https://api.imgpanel.com/v1/control/r2/migrations/cloudinary" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"assets": [
{
"context": {
"alt": "Hero image"
},
"folder": "legacy/home",
"public_id": "legacy/home/hero",
"secure_url": "https://res.cloudinary.com/demo/image/upload/sample.jpg",
"tags": [
"legacy",
"hero"
]
}
],
"folder": "cloudinary-import",
"metadata": {
"migration_batch": "legacy-site"
},
"overwrite": false,
"preset_id": "upr_example",
"tags": [
"cloudinary"
],
"unique_filename": false
}'Pull image resources directly from the Cloudinary Admin API into a retryable ImgPanel migration job. Credentials are used only for the request and are not stored. Set overwrite or unique_filename to control public_id conflicts.
curl -X POST "https://api.imgpanel.com/v1/control/r2/migrations/cloudinary/pull" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"api_key": "1234567890",
"api_secret": "not-stored",
"cloud_name": "demo",
"folder": "cloudinary-import",
"max_assets": 500,
"overwrite": false,
"page_size": 100,
"prefix": "legacy/",
"tags": [
"cloudinary"
],
"unique_filename": false
}'Pull image resources from Cloudinary with a saved encrypted connection instead of sending api_key/api_secret every time. Uses the same overwrite and unique_filename conflict policy as manifest migrations.
curl -X POST "https://api.imgpanel.com/v1/control/r2/migrations/cloudinary/pull" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{
"connection_id": "cld_...",
"folder": "cloudinary-import",
"max_assets": 500,
"overwrite": false,
"page_size": 100,
"prefix": "legacy/",
"tags": [
"cloudinary"
],
"unique_filename": false
}'Process the next batch of a Cloudinary migration job and return progress, imported count, failed count, skipped count, and item statuses.
curl -X POST "https://api.imgpanel.com/v1/control/r2/migrations/jobs/mjob_example/run" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{}'Read migration job progress and the first page of item statuses.
curl -X GET "https://api.imgpanel.com/v1/control/r2/migrations/jobs/mjob_example" \
-H "x-api-key: $IMGPANEL_API_KEY"Requeue failed Cloudinary migration items so the next run step retries only failed imports.
curl -X POST "https://api.imgpanel.com/v1/control/r2/migrations/jobs/mjob_example/retry-failed" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
--data '{}'Read recent migration job logs, including run steps, imported items, failed items, retry events, and cancellation.
curl -X GET "https://api.imgpanel.com/v1/control/r2/migrations/jobs/mjob_example/logs?limit=20" \
-H "x-api-key: $IMGPANEL_API_KEY"Read recent signed webhook delivery attempts.
curl -X GET "https://api.imgpanel.com/v1/control/webhook-deliveries?limit=20" \
-H "x-api-key: $IMGPANEL_API_KEY"Use Transform Studio, saved templates, or API options to generate Cloudflare-backed image variants.
width`w`, `width`Resize output width in pixels.`w_800` -> `{ "width": 800 }`height`h`, `height`Resize output height in pixels.`h_600` -> `{ "height": 600 }`fit`c`, `crop`, `fit`Cloudflare fit mode: scale-down, contain, cover, crop, pad.`c_fill` -> `{ "fit": "cover" }`gravity`g`, `gravity`Focus/crop gravity, including auto and focal-point workflows.`g_auto` -> `{ "gravity": "auto" }`format`f`, `format`Change encoded format for derived assets and delivery URLs.`f_webp` -> `{ "format": "webp" }`quality`q`, `quality`Compression quality or auto quality where supported.`q_80` -> `{ "quality": 80 }`blur`e_blur`, `blur`Apply blur effect.`e_blur:20` -> `{ "blur": 20 }`sharpen`e_sharpen`, `sharpen`Apply sharpening effect.`e_sharpen` -> `{ "sharpen": true }`rotate`a`, `angle`, `rotate`Rotate or auto-orient output.`a_90` -> `{ "rotate": 90 }`metadata`metadata`Control metadata stripping/preservation for delivery.`metadata=none`curl "https://api.imgpanel.com/v1/control/r2/cloudinary/resources/derived" \
-H "x-api-key: $IMGPANEL_API_KEY" \
-H "content-type: application/json" \
-d '{
"public_id": "catalog/hero",
"options": { "width": 1200, "format": "webp", "quality": 82 }
}'Transform Studio drawing tools export annotated images back to R2 as normal assets. Use the exported asset anywhere you use an uploaded file: collections, signed URLs, public delivery links, metadata review, or downstream derived jobs.
Website grab is designed for controlled imports and competitive audits, not infinite crawls. Treat each run as an observable job with reviewable results.
Crawler follows same-origin pages from the submitted URL and extracts image candidates from HTML, OpenGraph, CSS-backed sources where indexed, and direct image links.
Grab jobs are asynchronous. Use the dashboard job console or `/control/r2/grab/jobs` to review progress, cancel active jobs, and inspect messages.
Only image MIME types accepted by the R2 upload pipeline are stored. Imports derive stable public IDs from source paths, so `overwrite` and `unique_filename` control repeat-import conflicts.
Keep each run scoped to a real site section. Very large sites should be split into multiple jobs so retries and review stay manageable.
Remote servers can block or rate-limit requests. ImgPanel records skipped URLs and failures in job messages instead of silently importing partial data.
A practical cutover path from Cloudinary resources to ImgPanel R2 assets.
Create a Cloudinary connection in the dashboard with cloud name, API key, and API secret. Credentials are encrypted before storage.
Use Cloudinary search or a small manifest first. Confirm folder mapping, tags, context metadata, and conflict policy before a large pull.
Run a manifest import or Admin API pull. Choose whether public_id conflicts fail safely, overwrite existing R2 objects, or use unique_filename to keep both assets.
Use Jobs to see running, completed, failed, cancelled, and retried work across Cloudinary migration, website grab, and derived assets.
Apply saved transformation templates from Library or asset detail when you need R2-hosted derived assets instead of Cloudinary derived URLs.
Update application URLs after rename/move checks. Keep Cloudinary available until signed/private URLs and cache behavior are verified in production.
A small TypeScript helper for migration scripts that expect Cloudinary-shaped upload, admin, and search calls.
import { createImgPanelCloudinaryClient } from "@imgpanel/cloudinary-compat";
const cloudinary = createImgPanelCloudinaryClient({
apiKey: process.env.IMGPANEL_API_KEY!
});
await cloudinary.uploader.upload("https://example.com/hero.jpg", {
public_id: "legacy/home/hero",
tags: "legacy,hero",
overwrite: false
});
const search = await cloudinary.search
.expression("tags=hero AND metadata.campaign=summer")
.max_results(20)
.execute();The facade maps familiar methods like `uploader.upload`, `uploader.rename`, `api.resources`, `api.resource`, `api.delete_resources`, `api.restore`, and `search.expression().execute()` to ImgPanel's Cloudinary-compatible R2 endpoints.
The monorepo package entrypoint is `@imgpanel/cloudinary-compat`; it stays private until a public npm package name and release policy are chosen.
It is intentionally server-side and API-key based. It does not emulate Cloudinary delivery URL signing, client widgets, video APIs, add-ons, or billing/account APIs.
Cloudflare GraphQL and Logpush setup notes for CDN hit/miss reporting.
query ImgPanelCacheAttribution($zoneTag: string, $from: string, $to: string) {
viewer {
zones(filter: { zoneTag: $zoneTag }) {
httpRequestsAdaptiveGroups(
filter: {
datetime_geq: $from
datetime_lt: $to
}
limit: 100
) {
dimensions {
cacheStatus
clientRequestPath
coloCode
}
sum {
requests
edgeResponseBytes
}
}
}
}
}ImgPanel Usage currently records Worker/R2 delivery requests. To split CDN hit, miss, revalidated, and colo-level traffic, configure a Cloudflare zone id and an API token with Analytics Read.
GraphQL is the low-latency dashboard path. Set `CLOUDFLARE_ZONE_ID` and `CLOUDFLARE_ANALYTICS_API_TOKEN` (or `CLOUDFLARE_API_TOKEN`) on the API Worker, then `/control/r2/stats` returns `cache_attribution` buckets for cache status, colo, and top paths.
Logpush `http_requests` remains the raw archive path for long retention, replay, and warehouse analysis.
Give each integration the smallest useful scope set. Matching write scopes imply read access, for example `assets:write` can also read assets.
assets:readList, inspect, sign, and export assets.
assets:writeUpdate metadata, folders, tags, favorites, and derived asset links.
assets:deleteDelete, archive, restore, and purge assets.
uploads:writeUpload files, import URLs, issue direct-upload tokens, and run grab jobs.
search:readRun Cloudinary-style expression search over indexed assets.
folders:readRead folder lists and counts.
collections:readRead collections and collection assets.
collections:writeCreate, update, share, and manage collection assets.
transforms:readRead transformation templates and derived assets.
transforms:writeCreate templates and generate derived assets.
webhooks:readRead webhook endpoints and delivery logs.
webhooks:writeCreate, test, rotate, and delete webhook endpoints.
admin:readRead workspace context, stats, activity, and account-level config.
admin:writeManage workspace-level configuration.
401 unauthorizedNo dashboard session or API key was provided.401 invalid_api_keyThe supplied `ik_live_...` key does not match any stored hash.401 api_key_disabledThe key is disabled or revoked.401 api_key_expiredThe key expiration date is in the past.403 missing_api_key_scopeThe key exists, but does not include a required scope for this endpoint.403 session_requiredThis route is dashboard-only and cannot be called with an API key.