Search by

assetkiwi / integration-drupal

asset.kiwi

Connects to asset.kiwi digital asset management platform with asset browsing, usage tracking, webhook sync, and image formatting.

Package info

github.com/assetkiwi/integration-drupal

Homepage

Type:drupal-module

pkg:composer/assetkiwi/integration-drupal

Statistics

Installs: 14

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-07-15 09:32 UTC

This package is auto-updated.

Last update: 2026-11-07 19:06:39 UTC


README

Integrates Drupal with the asset.kiwi digital asset management platform. Browse, import, and sync assets directly from the Drupal media library — with automatic usage tracking back to asset.kiwi so you always know where assets are used.

Features

  • Media source plugin — Manage asset.kiwi assets as native Drupal media entities
  • asset.kiwi Remote Image source — Image media for Canvas, image fields and image styles, served and resized by asset.kiwi (see below)
  • Visual picker widget — Browse, search, and filter assets (by collection, tag, type, and semantic search mode) from a self-contained widget embedded in the Media Library add form and the field widget's dialog — no page reloads, no DAM API token in the browser
  • Field widget — Select assets on any content type using the "asset.kiwi Browser" widget
  • Image formatter — Render public images from stable, resizable asset.kiwi URLs with a srcset, with lazy/eager loading and optional link-to-original
  • Webhook sync — asset.kiwi pushes updates (rename, alt text, description changes, deletion) to keep Drupal in sync
  • Usage tracking — Whenever a media entity is inserted, updated, or deleted, asset.kiwi is notified so it knows where every asset lives
  • Drush integration — Batch-import assets from the CLI with full filter support
  • Media Library integration — A custom "Add" form replaces the default upload tab in the Media Library for asset.kiwi media types
  • File download on import — Assets can be downloaded locally on import for use with Drupal image styles and responsive images

Requirements

  • Drupal 11 (^11)
  • PHP 8.3 or later (required by Drupal 11)
  • Drupal core modules: Media (media), Media Library (media_library)
  • Drush 13+ (optional, for the assetkiwi:import-assets command)
  • An asset.kiwi account with a valid API token

Installation

Install via Composer:

composer require drupal/assetkiwi_connect

Or manually place the module in modules/contrib/assetkiwi_connect/.

Enable the module:

drush en assetkiwi_connect -y

Or visit Extend (/admin/modules) in the Drupal admin UI, find "asset.kiwi Connect" under the Media package, and enable it.

Configuration

Navigate to Configuration → Media → asset.kiwi Settings (/admin/config/media/assetkiwi).

API Connection

Setting Description
API Base URL The base URL of your asset.kiwi instance (e.g., https://assetkiwi.ddev.site). Do not include a trailing slash.
API Token An API token from the asset.kiwi Settings page. Required for all API requests.
Cache Lifetime How long to cache asset thumbnails locally, in seconds. Set to 0 to disable caching. Default: 3600 (1 hour).
Signing key Optional. The workspace key from Manage → Dynamic Delivery → Signed URLs in asset.kiwi. Lets the module sign transform URLs that visitors could not request otherwise (see Signed transform URLs). Leave empty to keep the stored key.
Allow uploading to asset.kiwi Shows an Upload to asset.kiwi option in the Media Library for asset.kiwi media types. Users also need the Upload files to asset.kiwi permission, and the token must have the assets:write ability (per-user OAuth: the assets:write scope). When off, the option is hidden and uploads are rejected server-side. Default: on.

OAuth2 Authentication (Per-User Mode)

asset.kiwi Connect supports two authentication modes, toggled via the Authentication Mode setting.

Shared Token Mode (default)

One API token is stored in module config. All Drupal users see the same assets and all API requests use the shared token. This is the simplest setup and works well for single-user Drupal sites or when all content editors share the same DAM access level.

Per-User OAuth Mode

Each Drupal user gets their own DAM identity via OAuth2 (authorization code grant with PKCE). When a user accesses an asset.kiwi-related page (media library, asset browser), they are prompted to connect their account. Once authorized, their personal access token is used for all API requests — different users can have different asset access scopes.

Enabling per-user OAuth:

  1. In the asset.kiwi admin panel, create an OAuth2 client:

    • Navigate to Settings → OAuth Clients (or the OAuth2 management page)
    • Create a new client with the authorization code grant type
    • Set the redirect URI to https://your-drupal-site.ddev.site/assetkiwi/oauth/callback
    • Note the Client ID and Client Secret
  2. In Drupal, navigate to Configuration → Media → asset.kiwi Settings (/admin/config/media/assetkiwi)

  3. Under OAuth2 Authentication, set Authentication Mode to "Per-user OAuth"

  4. Fill in the OAuth fields:

Setting Description
OAuth Client ID The client ID from the OAuth2 application registered in asset.kiwi
OAuth Client Secret The client secret from the registered OAuth2 application
Authorization URL The OAuth2 authorization endpoint (e.g., https://dam.example.com/oauth/authorize)
Token URL The OAuth2 token endpoint (e.g., https://dam.example.com/oauth/token)
OAuth Scopes The scopes to request. At minimum, assets:read is required for browsing and importing assets. Check assets:write if users need upload/modify permissions
  1. Save the configuration

User experience:

  • When a user visits an asset.kiwi-related page (media library, asset browser widget), a warning message appears: "Your asset.kiwi account is not connected. Connect now to access your assets."
  • Clicking Connect now redirects the user to the asset.kiwi authorization screen
  • After approving, the user is redirected back to Drupal and their access token is stored
  • Subsequent API requests use the user's personal token instead of the shared token
  • If a user's token expires or is revoked, they are prompted to reconnect
  • Users who have not yet connected fall back to the shared API token (if one is configured)

Webhook

Setting Description
Webhook Secret A shared secret for verifying incoming webhook payloads from asset.kiwi. Must match the secret configured in the asset.kiwi webhook settings. Leave empty to disable webhook verification (not recommended).

Image Style Mapping

Map Drupal image styles to asset.kiwi variant names. When using the asset.kiwi Image formatter with a mapped variant, the module uses the pre-rendered variant URL from asset.kiwi rather than processing the image locally.

Drupal Style Default Variant Description
Thumbnail thumb Small preview image
Medium medium Mid-size display
Large large Full-size display

These mappings are used by the media source plugin to populate thumbnail_uri, thumb_url, medium_url, and large_url metadata.

Test Connection

Click Test Connection at the bottom of the settings form to verify your API URL and token. A success message confirms the API responded; a warning indicates connection issues.

Setting Up a Media Type

After configuring the connection, create a media type that pulls from asset.kiwi.

Step 1: Create the Media Type

  1. Go to Structure → Media types (/admin/structure/media)
  2. Click Add media type
  3. Give it a name (e.g., "asset.kiwi Asset") and machine name (e.g., assetkiwi_asset)
  4. Under Media source, select asset.kiwi Asset
  5. Click Save

Step 2: Configure the Source

On the media type edit page, under the asset.kiwi Asset source settings:

  • Allowed asset types — Check the asset types this media type should accept (Images, Video, Audio, Documents). If none are checked, all types are allowed. When restricted, the browser widget and media library add form will only show and allow selecting assets of the chosen types.

Step 3: Map Metadata Fields

Add fields to your media type that map to asset.kiwi metadata. The source plugin provides the following metadata attributes:

Metadata Attribute Description Suggested Field Type
uuid Asset UUID Text (plain)
original_name Original filename Text (plain)
mime_type MIME type Text (plain)
size File size in bytes Number (integer)
url Asset URL Text (plain)
alt_text Alt text Text (plain)
description Description Text (long)
width Width in pixels Number (integer)
height Height in pixels Number (integer)
thumbnail_uri Local thumbnail path (automatically populated)
thumb_url Thumbnail variant URL Text (plain)
medium_url Medium variant URL Text (plain)
large_url Large variant URL Text (plain)

In field settings, under Metadata mapping, map the asset.kiwi attributes to your field names. The source field (field_assetkiwi_uuid) is created automatically and stores the asset UUID.

Tip: For image assets, add an Image field (e.g., field_media_image). When an asset is imported and the downloaded file is an image, the module automatically populates this field with the downloaded file.

Step 4: Configure Form Display

On the Form display tab for your media type, set the widget for the source field (field_assetkiwi_uuid) to asset.kiwi Browser. This replaces the plain text field with a "Browse DAM" button that opens the visual asset browser.

Step 5: Configure Display

On the Display tab, set the formatter for the source field to asset.kiwi Image. Configure:

  • Variant name — The asset.kiwi variant to render (e.g., thumb, medium, large). Leave empty for the original.
  • Image loading — Lazy or eager.
  • Link to original asset — Whether to wrap the image in a link to the original file.

asset.kiwi Remote Image: Images for Canvas and Image Fields

The asset.kiwi Asset source stores only the asset UUID. Anything that only accepts core image media therefore cannot use it: Canvas image props (heroes, cards, the image component), core image formatters and image styles, and metatag image tokens.

For those, use the asset.kiwi Remote Image source. It builds on core's Image source, so it is accepted wherever image media is, and it is still served by asset.kiwi:

  • The image field references a file with an assetkiwi://<uuid>/<filename> URI. Nothing is downloaded or stored locally.
  • The file URL is the asset's asset.kiwi URL (embed_url for public assets).
  • Image style URLs, including Canvas's srcset widths, map to asset.kiwi's transform URL with the style's width (?w=…). A style with a crop effect (Crop, Scale and crop) becomes an asset.kiwi crop at the nearest ratio your workspace offers, with the width rounded up to a width step (?w=…&h=…&fit=crop, focal point decides what stays in view; ratios beyond 1:20 or 20:1 are not cropped), and a Convert effect to WebP, AVIF or JPEG sets &format=. No derivatives are generated locally.
  • Width, height and alt text come from the API on import. The asset UUID is kept in field_assetkiwi_uuid, so usage tracking and webhooks work as for asset.kiwi Asset media.

Setting it up

  1. Create a media type with the asset.kiwi Remote Image source. The field_assetkiwi_uuid field is added automatically.
  2. Pick images in the Media Library as usual: the asset.kiwi tab only shows images for this media type.
  3. Canvas: rebuild caches (drush cr) after creating the media type. Canvas then creates new versions of components with image props that accept it. Component instances placed earlier keep their version until they are edited.

Limitations

  • Public assets only. Pages show these images from stable URLs, which only public assets have. Other assets are refused when picking or importing, and their signed URLs (which expire and grant access) never appear in a page.
  • Image styles pass their width, crop ratio and convert format only. Other effects (rotate, desaturate, …) are not applied. Canvas's parametrized {width} URLs keep the original ratio.
  • Do not delete an image media type that Canvas components reference. Canvas switches components that use it to a fallback and deletes unused components with image props. This is Canvas behavior for every image media type.

Usage

Browsing and Importing from the Media Library

  1. Edit any content that has a media reference field
  2. Click Add media to open the Media Library
  3. Select the tab for your asset.kiwi media type — the asset.kiwi picker widget is the only thing shown for this tab (Drupal's own "existing media" browse grid is suppressed for asset.kiwi media types, to avoid two separate, conflicting selection UIs for the same asset)
  4. Click an asset's checkbox to select it (or several — up to the host field's configured cardinality), then click Use selected. Assets already imported as Drupal media show an Already added badge — selecting one of those references the existing media entity rather than creating a duplicate
  5. For genuinely new assets, media entities are created (unsaved) with metadata pre-populated from asset.kiwi; review/fill in any required fields, then Save and select/Save and insert as usual

Uploading new files to asset.kiwi

With Allow uploading to asset.kiwi enabled and the Upload files to asset.kiwi permission, the asset.kiwi tab in the Media Library has an Upload to asset.kiwi section below the picker. Choose a file (allowed extensions follow the media type's Allowed asset types; size is capped by PHP's upload limit and asset.kiwi's 512 MB limit). The file is sent to asset.kiwi (POST /api/v1/assets), the temporary Drupal copy is deleted, and you continue to the same review step as for picked assets, then Save and insert. If asset.kiwi already has an identical file, that existing asset is reused (and an existing Drupal media item for it is inserted directly).

Using the asset.kiwi Browser Field Widget

The "asset.kiwi Browser" widget can be placed on any entity type that has a plain text field — not just media. When configured:

  1. A "Browse DAM" button appears in place of the text input
  2. Clicking it opens the asset.kiwi picker widget in a Drupal dialog
  3. Clicking an asset selects it immediately and closes the dialog, populating the hidden field with the asset UUID
  4. A preview thumbnail and the "Replace asset" / "Remove" buttons appear for the selected asset — including on page load for content that already has a reference

Both entry points are powered by the same picker widget (js/assetkiwi-picker.js), which calls the module's own JSON endpoints (/admin/assetkiwi/api/assets, /admin/assetkiwi/api/facets) rather than talking to asset.kiwi directly — the API token never reaches the browser.

Rendering Assets with the asset.kiwi Image Formatter

The formatter works on any string field containing an asset.kiwi asset UUID. It looks up the asset and renders an <img> tag:

  • Public assets (the API returns an embed_url) render from stable URLs that never expire. With no style configured, the image gets src and srcset from {api_url}/media/serve/{uuid}/transform?w=N over the widths the API lists in image_transform.widths (160 to 2560 px), up to the original width. A configured image style renders through /media/serve/{uuid}/transform/{style id}.
  • Other assets fall back to the API's signed URLs, which expire after about an hour. That output is cached for at most 50 minutes and the page is kept out of the anonymous page cache, so pages never hold a dead URL. Visitors may still be refused once the URL expires, so make assets public before you use them on public pages; the picker marks assets that are not public.
  • API errors render nothing, are never cached, and are retried on the next request.

Asset responses are cached for 50 minutes; the asset.updated, asset.deleted and asset.status_changed webhooks clear them (cache tag assetkiwi_asset:<uuid>). The audio, video and file link formatters follow the same rules, using the asset's embed_url when it is public.

With no style configured, three formatter settings shape the srcset URLs:

Setting Effect
Aspect ratio None, or one of 1:1, 4:5, 3:4, 2:3, 9:16, 4:3, 3:2, 16:9, 21:9. Each candidate becomes ?w=W&h=round(W/ratio)&fit=crop, no wider than the source can fill, and the <img> gets width/height for that ratio. asset.kiwi picks the crop (focal point, else smart crop).
Format Default, WebP, AVIF or JPEG (&format=).
Quality Optional, 1-100 (&q=); visitors get the nearest quality step.

The URLs use the exact canonical form asset.kiwi expects, so they never redirect. A ratio your workspace doesn't offer is signed when a signing key is set; without one, asset.kiwi snaps it to the nearest offered ratio. In code, \Drupal::service('assetkiwi_connect.client')->imageAttributes($data, '16:9', 'webp', 80) returns the same src, srcset, sizes, width and height ($data from normalizeAsset()), and getTransformUrl($uuid, $options) also accepts dpr.

The DynamicDelivery options (width, height, fit, format, quality) build an ad-hoc transform URL. Visitors without an API token only get the width and height (snapped to the allowed widths); fit accepts fit, fill, crop and auto.

Signed transform URLs

getSignedTransformUrl(string $uuid, array $params, ?int $expiresAt = NULL): ?string returns a transform URL that asset.kiwi renders with exactly these parameters (w, h, fit, q, format, gravity, dpr), without visitor snapping. $expiresAt (unix timestamp) makes it stop working after that moment. It returns NULL when no signing key is configured.

$url = \Drupal::service('assetkiwi_connect.client')
  ->getSignedTransformUrl($uuid, ['w' => 500, 'h' => 333, 'fit' => 'crop', 'gravity' => 'fp:0.3:0.5'], time() + 3600);

Security notes:

  • The signing key is a secret. It is used only on the server and never sent to the browser; only the signature (&s=…) is.
  • Anyone with a signed URL can fetch that one variant until it expires. A signed URL without $expiresAt never expires: the only way to revoke it is to rotate the key, which revokes every URL signed with it. Use $expiresAt for anything that shouldn't stay available.
  • The formatter signs only ratios from its fixed list that your workspace doesn't advertise. Image styles (and the Remote Image source) are never signed: their crop ratio snaps to the nearest advertised ratio, so whoever can edit an image style can't make the site sign arbitrary transforms.
  • Changing or removing the key (Remove stored signing key on the settings page) clears rendered pages, so they don't keep URLs with old signatures.
  • A transform signature only fixes the parameters; it is not one of the API's signed asset URLs, which grant access to assets that are not public. Those still never appear in rendered pages.
  • Like the API token, a key entered in the form is stored in assetkiwi_connect.settings, so it ends up in exported config. Keep the key in settings.php instead ($config['assetkiwi_connect.settings']['signing_key'] = getenv('ASSETKIWI_SIGNING_KEY');) and leave the form field empty (after changing it there, run drush cr so pages drop old signatures). If it leaks, rotate it in asset.kiwi: every URL signed with the old key stops working.

Webhook Sync

asset.kiwi can push real-time updates to Drupal via webhooks. The endpoint is:

POST /assetkiwi/webhook

Configure your asset.kiwi instance to send webhooks to https://your-drupal-site.ddev.site/assetkiwi/webhook, and set the same secret in both asset.kiwi and the Drupal settings form.

Handled events:

  • asset.updated — Syncs name, field_assetkiwi_alt, and field_assetkiwi_description for all media entities referencing the asset UUID
  • asset.deleted — Unpublishes (does not delete) media entities referencing the deleted asset to preserve existing references
  • asset.status_changed — When the asset is archived (by hand, or because its usage rights expired), unpublishes its media entities like a deleted asset; other status changes only clear the cache. Coming back from the archive is not mirrored: an editor republishes the media on purpose.

Without the webhook, Drupal only notices an archived or expired asset when its file stops loading. The status report (Reports › Status report) shows a warning until the webhook secret is set.

  • webhook.test — Responds 200 OK for verification

Webhook payloads are verified using HMAC-SHA256 signature comparison. Unverified requests return a 403 response.

Usage Tracking

Usage tracking is automatic. On every media entity insert, update, or delete, the module sends the asset UUID, entity type, entity ID, and canonical URL to asset.kiwi's usage endpoint (POST /api/v1/assets/{uuid}/usage). When a media entity is deleted, the usage record is removed via DELETE.

asset.kiwi then surfaces this data in its admin panel, showing which Drupal entities reference each asset.

Drush Commands

assetkiwi:import-assets

Import assets from asset.kiwi into Drupal as media entities.

drush assetkiwi:import-assets <bundle> [options]

Arguments:

Argument Description
bundle The media type machine name (e.g., assetkiwi_asset)

Options:

Option Description
--collection Import from a specific collection slug
--tag Import assets with a specific tag slug
--type Filter by asset type: image, video, audio, or document
--search Search query to filter assets
--limit Maximum number of assets to import (0 = unlimited)
--dry-run Show what would be imported without actually importing
--uuid Import a single asset by UUID

Examples:

# Import all assets from the "marketing-images" collection
drush assetkiwi:import-assets assetkiwi_asset --collection=marketing-images

# Import up to 10 assets tagged "hero"
drush assetkiwi:import-assets assetkiwi_asset --tag=hero --limit=10

# Import only image assets with a search term
drush assetkiwi:import-assets assetkiwi_asset --type=image --search="banner"

# Preview what importing a specific UUID would do
drush assetkiwi:import-assets assetkiwi_asset --uuid=abc-123-def --dry-run

# Import a single asset
drush assetkiwi:import-assets assetkiwi_asset --uuid=abc-123-def

The command handles pagination automatically for large result sets. Existing assets (matched by UUID) are skipped. For image assets, the original file is downloaded and attached to the media entity.

Permissions

Permission Description
Administer asset.kiwi settings Configure the API URL, token, webhook secret, and image style mapping
Browse asset.kiwi assets Use the picker widget (Media Library add form and field widget) and select assets for import
Upload files to asset.kiwi Upload new files to asset.kiwi from the Media Library (also needs the Allow uploading to asset.kiwi setting)

Both permissions are typically assigned to content editors and administrators. The browse permission is required for the media library add form and the field widget to function.

Extending

Custom Field Mappings

The media source plugin (AssetKiwiAsset) provides metadata through getMetadata(). To add custom field mappings:

  1. Add a new field to your asset.kiwi media type
  2. Map it in the media type's field map under Metadata mapping — select the corresponding asset.kiwi attribute from the dropdown
  3. If the attribute does not exist in getMetadata(), extend the source plugin:
// mymodule/src/Plugin/media/Source/CustomAssetKiwiAsset.php
namespace Drupal\mymodule\Plugin\media\Source;

use Drupal\assetkiwi_connect\Plugin\media\Source\AssetKiwiAsset;

/**
 * @MediaSource(
 *   id = "custom_assetkiwi_asset",
 *   label = @Translation("Custom asset.kiwi Asset"),
 * )
 */
class CustomAssetKiwiAsset extends AssetKiwiAsset {

  public function getMetadataAttributes(): array {
    $attributes = parent::getMetadataAttributes();
    $attributes['copyright'] = $this->t('Copyright');
    $attributes['photographer'] = $this->t('Photographer');
    return $attributes;
  }

  public function getMetadata(MediaInterface $media, $name): mixed {
    $value = match ($name) {
      'copyright' => $this->fetchCustomField($media, 'copyright'),
      'photographer' => $this->fetchCustomField($media, 'photographer'),
      default => parent::getMetadata($media, $name),
    };
    return $value;
  }

}

Custom Widget or Formatter

The "asset.kiwi Browser" widget and "asset.kiwi Image" formatter work on any string field containing an asset UUID. You can use them on non-media entity types (nodes, paragraphs, blocks) by adding a plain text field and setting the widget/formatter accordingly.

Reacting to Webhook Events

Webhook events are dispatched through \Drupal\assetkiwi_connect\Controller\WebhookController. To react to additional events or extend handling, use Drupal's event system:

// mymodule.services.yml
services:
  mymodule.webhook_subscriber:
    class: Drupal\mymodule\EventSubscriber\WebhookSubscriber
    tags:
      - { name: event_subscriber }

Or alter the webhook controller via a service decorator if deeper integration is needed.

Troubleshooting

Connection Refused / API Errors

Symptom: "Could not load assets from asset.kiwi." message in the picker widget, or empty asset grids.

Checklist:

  1. Verify the API Base URL is correct and reachable from the Drupal server
  2. Verify the API Token has not expired or been revoked
  3. Check Drupal's recent log messages (/admin/reports/dblog) for assetkiwi_connect errors
  4. Test connectivity from the server: curl -H "Authorization: Bearer TOKEN" https://your-dam.example.com/api/v1/assets
  5. Use the Test Connection button on the settings form

Empty Picker / No Assets Found

Symptom: The picker widget loads but shows "No assets found" even though assets exist.

Checklist:

  1. Confirm the API token has access to the expected assets
  2. Check that any type restrictions on the media type source configuration aren't filtering out assets unexpectedly
  3. Try clearing the type/collection/tag filters in the picker
  4. If using the document type filter, ensure your asset.kiwi instance version supports the type parameter for documents (the module automatically translates mime_type=document to type=document)
  5. Check the Search mode — if set to "Semantic" or "Hybrid", verify your asset.kiwi instance has semantic search enabled

Webhook 403 Errors

Symptom: asset.kiwi reports 403 responses when sending webhooks to Drupal.

Checklist:

  1. Ensure the Webhook Secret is configured in Drupal settings (not empty)
  2. Verify the secret in Drupal matches the secret in asset.kiwi's webhook configuration exactly (case-sensitive, no extra whitespace)
  3. Check that the webhook URL is reachable from the asset.kiwi server (not blocked by firewall, VPN, or basic auth)
  4. If Drupal is behind HTTP basic auth, the webhook endpoint is excluded from auth by default (route has _access: 'TRUE')
  5. Check Drupal's logs for "Webhook signature verification failed" entries
  6. Test manually:
PAYLOAD='{"event":"webhook.test","data":{}}'
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "your-secret" | cut -d' ' -f2)
curl -X POST -H "X-Webhook-Signature: $SIGNATURE" \
  -H "Content-Type: application/json" \
  -d "$PAYLOAD" \
  https://your-drupal-site.ddev.site/assetkiwi/webhook

Image Styles Not Working

Symptom: Drupal image styles don't generate derivatives for asset.kiwi images.

Solution: The asset.kiwi Image formatter renders images from asset.kiwi's variant URLs by default (bypassing Drupal image styles). To use Drupal image styles, you have two options:

  1. Use the imported local file — download the asset file on import (automatic for image assets via the media library add form) and use Drupal's standard Image formatter on the field_media_image field
  2. Map Drupal image styles to asset.kiwi variants in the settings form, so each style pulls the corresponding variant URL

Import Failures

Symptom: Drush assetkiwi:import-assets reports errors or skips assets.

Checklist:

  1. Verify the media type exists and uses the asset.kiwi Asset source plugin
  2. Run with --dry-run first to preview what would happen
  3. Check that assets aren't already imported (duplicate UUIDs are skipped)
  4. For file download failures, check write permissions on public://assetkiwi_files/
  5. Review Drupal's logs for detailed error messages

Migration Submodule (assetkiwi_connect_migrate)

The assetkiwi_connect_migrate submodule migrates locally-stored Drupal media files to asset.kiwi.

Pipeline

The migration follows this pipeline for each media item:

  1. Hash — Computes a content hash of the local file
  2. Upload — Uploads the file to asset.kiwi (content-addressed, duplicates are reused)
  3. Verify — Confirms the remote file size matches the local file
  4. Offload — Marks the media as offloaded; rendering is swapped to use asset.kiwi URLs

Local files are NOT deleted during migration. Use the separate purge step to remove local files after verifying.

Usage

Via Drush

drush assetkiwi-migrate:scan    # Find eligible media files
drush assetkiwi-migrate:run     # Migrate all pending items
drush assetkiwi-migrate:status  # View migration status counts
drush assetkiwi-migrate:purge   # Delete local files for offloaded media

Via Admin UI

Navigate to Media → AssetKiwi Migration. Use the Scan, Migrate, and Purge buttons.

Tracking Table

Migration status is tracked in the assetkiwi_connect_migrate database table with statuses: pending, uploading, uploaded, verified, offloaded, failed, purged.

Important

  • Requires the main assetkiwi_connect module to be installed and configured
  • Migration is idempotent — re-running on already-migrated items is safe
  • The original Drupal file field values are never modified; rendering is swapped at the view layer

Changelog

Unreleased

  • Images render from stable /media/serve/{uuid}/transform URLs with a srcset for public assets; audio, video and file links use embed_url. Signed URLs are only used for assets that are not public, and their output is cached below their lifetime with the page cache switched off.
  • Transform URLs moved from the deprecated /api/v1/media/... alias to /media/serve/....
  • Image styles are passed to the transform-by-style URL by id (keys returned 404).
  • The fit options fill-down and fit-down (rejected by the server) were removed; saved values map to fill and fit.
  • getAsset() and getImageStyles() are cached for 50 minutes; failures are not cached and switch off the render and page cache for that request.
  • The picker marks assets that are not public.