Uxbee.Umbraco.Bynder
17.0.0
dotnet add package Uxbee.Umbraco.Bynder --version 17.0.0
NuGet\Install-Package Uxbee.Umbraco.Bynder -Version 17.0.0
<PackageReference Include="Uxbee.Umbraco.Bynder" Version="17.0.0" />
<PackageVersion Include="Uxbee.Umbraco.Bynder" Version="17.0.0" />
<PackageReference Include="Uxbee.Umbraco.Bynder" />
paket add Uxbee.Umbraco.Bynder --version 17.0.0
#r "nuget: Uxbee.Umbraco.Bynder, 17.0.0"
#:package Uxbee.Umbraco.Bynder@17.0.0
#addin nuget:?package=Uxbee.Umbraco.Bynder&version=17.0.0
#tool nuget:?package=Uxbee.Umbraco.Bynder&version=17.0.0
Bynder connector for Umbraco
Connects Umbraco CMS to the Bynder Digital Asset Management (DAM) platform, letting editors browse, select and reuse Bynder assets directly from the Umbraco backoffice - modelled on Umbraco HQ's own Aprimo DAM integration.
Features
- Bynder Asset Picker property editor - lets editors pick one or more Bynder assets per property. When "Multiple" is on, Compact View's own single-popup
MultiSelectgrid is used - one picking session, several assets at once (configurable min/max, allowed asset types, language, and several Universal Compact View filter/behaviour settings). - Bynder File Picker property editor - picks one file (the default) or, optionally, more than one (its own "Multiple"/min-max settings) via Compact View's
SingleSelectFilemode - each pick is still one asset at a time, even with Multiple on, since the omnichannel output/derivative picker only exists in that single-select mode. Every pick exposes Bynder's omnichannel output selection: the editor chooses not just an asset but a specific rendition/output for it (e.g. a crop or format tailored to a particular channel). - Picking is done via Bynder's own Universal Compact View widget. Each editor authenticates directly with their own Bynder account inside the widget itself (Compact View's own interactive login) - this package never stores a Bynder connection of its own, so there's nothing to keep alive or reconnect.
- Strongly-typed
BynderAsset/BynderFilemodels (or the source-agnosticBynderOrMediaAsset/BynderOrMediaFile, when a property can also hold a native Umbraco Media pick) available in templates via property value converters, plusHtml.RenderBynderAsset(...)/Html.RenderBynderFile(...)/Html.RenderBynderOrMediaAsset(...)/Html.RenderBynderOrMediaFile(...)helpers for quick rendering. Rendering always uses a public derivative/output URL - never the raw original file, which isn't guaranteed to be publicly accessible. Both pickers also implement Umbraco's Delivery API for headless consumers - see Rendering assets below for the exact shape each one resolves to. - A Bynder capability for the rich text editor (Capabilities tab > Embeds and media, off by default like every capability) - lets editors embed a Bynder asset directly in rich text. It supports IMAGE and DOCUMENT (video and audio aren't supported in rich text - use the pickers for those); a developer can narrow this down install-wide via
Bynder:RTEAssetTypes, see Configuration below. - Asset Tracking (optional, off by default) - reports which pages use which Bynder assets, both to Bynder itself (via its Asset Usage API) and in a Settings > Bynder dashboard/per-page content tab. See Asset Tracking below.
- Meta Data Sync (optional, off by default) - keeps a picked Bynder asset's alt text/copyright in sync with its current Bynder metadata, without needing a content republish. See Meta Data Sync below.
- Transcript Sync (optional, off by default) - turns a VIDEO/AUDIO asset's Bynder-generated Speech-to-Text transcript into a ready-to-use WebVTT subtitle track. See Transcript Sync below.
- Assets are stored by reference (id, name, thumbnail/derivative URLs) - not copied into Umbraco's media library.
Requirements
- Umbraco CMS 17.x - every 17 release, 17.0.0 or later (
[17.0.0, 18.0.0)) - A Bynder portal, and editors with their own Bynder user accounts (they'll be prompted to log in the first time they open the picker)
- A valid license API key - see License API key below. Without one, the package is entirely inactive: no picker can open and the Bynder portal domain can't be configured.
Versioning
This package's own version number follows {Umbraco major}.{minor}.{patch} -
the same convention Umbraco's own first-party add-ons use (Forms, Deploy,
Commerce, Workflow, aligned since Umbraco 9) and that community packages like
uSync already follow:
- Major tracks the Umbraco CMS major version this release targets (e.g.
17.x.xtargets Umbraco 17). It only changes when support for a new Umbraco major has been verified - it is not what the Umbraco Marketplace uses to determine compatibility; that's derived entirely from this package's own NuGet dependency range onUmbraco.Cms.Core(see Requirements above), independent of this version number. - Minor is this package's own counter for a new backwards-compatible feature.
- Patch is this package's own counter for a hotfix.
Neither minor nor patch tracks Umbraco's own minor/patch releases - a new Umbraco 17.x patch doesn't by itself require a new release of this package, since the dependency range above already states the full range of Umbraco 17.x versions this package supports.
Installation
dotnet add package Uxbee.Umbraco.Bynder
Configuration
License API key
This package requires a valid license API key before anything else works. Register an account at bynder.integrationlayer.io to start a free two-month trial and get a key; after the trial, licenses are purchased from Bynder and the key for your subscription is created on the same portal. The same license also covers the @uxbee/bynder-sdk package - no separate license is needed. There are three ways to set it, in this priority order:
appsettings.json (recommended for most installs):
{ "Bynder": { "LicenseAPIKey": "your-license-api-key" } }The property name matches case-insensitively, so
LicenseApiKey/LicenseAPIKey/licenseapikeyare all equivalent - use whichever casing your own config conventions prefer. The standardBynder__LicenseApiKeydouble-underscore environment variable convention (same one every other Bynder setting already supports) sets this same value too.A single, flat
BYNDER_LICENSE_API_KEYenvironment variable - for installs that prefer one variable with no hierarchy. Only used when appsettings.json hasn't set a value.Settings section > Bynder > Configuration's "License" box (admin-only) - only available when neither of the above two is set; the box shows appsettings/environment-locked keys as read-only once one of them is set, or hides itself entirely once a locked key is confirmed valid (it reappears if a locked key ever becomes invalid, so the problem stays visible).
Whichever source it comes from, the key is validated immediately when set (or, for appsettings/environment-configured keys, shortly after the app starts), and re-checked periodically afterwards - a revoked/expired key eventually deactivates the package again without needing a redeploy. If the licensing service itself can't be reached, a key that was confirmed valid within the last 72 hours keeps working, so a short outage on the licensing side doesn't lock editors out. Content already published on your site keeps working either way - Bynder assets are served from Bynder's own links; only the backoffice (the Bynder Universal Compact View, sync and tracking) stops. Without a valid key from any of the three, the Bynder portal domain always resolves as unconfigured, regardless of what's actually set for it (see below) - no picker can open, and no OAuth client ID/secret can be tested or used.
Besides the license key itself, the only other thing this package needs configured is the Bynder portal domain - no OAuth app registration or client secret for that, since editors authenticate with Bynder directly, inside the picker, using their own Bynder credentials. There are two ways to set it:
Option 1: appsettings.json (recommended)
Add a Bynder section - just the portal domain, nothing secret:
{
"Bynder": {
"PortalDomain": "yourcompany.getbynder.com"
}
}
This is the deploy-controlled, authoritative source: once it's set here, it always wins over any value set from the backoffice (see Option 2), and a redeploy is needed to change it.
Option 2: Settings > Bynder dashboard
If Bynder:PortalDomain isn't set in appsettings, an admin can set it from the backoffice instead: Settings section > Bynder. This is meant for installs where nobody has added it to appsettings yet - it saves the domain to the database (via IKeyValueService), so the pickers work without a redeploy.
This dashboard is only visible to admins, and only editable while appsettings hasn't set a value - once Bynder:PortalDomain is added to appsettings, the dashboard switches to showing that value read-only, with a note that it's config-controlled.
Optionally, restrict which asset types the rich text editor's "Bynder" capability offers. It supports IMAGE and DOCUMENT only - both are enabled by default - so this setting can only narrow that down to one of the two:
{
"Bynder": {
"PortalDomain": "yourcompany.getbynder.com",
"RTEAssetTypes": ["IMAGE"]
}
}
VIDEO and AUDIO are not supported in the rich text editor, and won't be - embedding a player
inside rich text has too many implications. Use the Bynder Asset Picker or File Picker property
editors for video and audio instead. DOCUMENT renders as a link to the asset's own original
file, and that asset must be marked Public in Bynder for the link to work for an anonymous
site visitor (a non-Public asset's original file requires an authenticated Bynder session, so the
link 404s/redirects to a Bynder login page otherwise).
Rendering assets
@using Uxbee.Umbraco.Bynder.Helpers
@using Uxbee.Umbraco.Bynder.Models
@{
var asset = Model.Value<BynderAsset>("heroImage");
}
@Html.RenderBynderAsset(asset, derivative: "webImage", cssClass: "hero-image")
For a VIDEO asset, RenderBynderAsset/RenderBynderOrMediaAsset/RenderBynderFile/RenderBynderOrMediaFile all render controls but, by default, also controlsList="nodownload" on the <video> tag - hiding the native browser download affordance a plain controls player otherwise offers. Pass allowDownload: true to offer it. This is a UI convenience only (controlsList isn't a real access restriction - the file is still reachable at its own src), not something enforced anywhere else.
If the same property could also hold a native Umbraco Media pick (i.e. it's a Bynder Asset Picker Data Type an editor can swap for the native Media Picker without breaking already-saved content), use the source-agnostic BynderOrMediaAsset/Html.RenderBynderOrMediaAsset(...) instead - Source is "bynder" or "media", and Url()/CanEmbedPlayer already switch on it so a template doesn't have to:
@{
var audio = Model.Value<BynderOrMediaAsset>("backgroundAudio");
}
@if (!string.IsNullOrWhiteSpace(audio?.Url()) && audio.CanEmbedPlayer)
{
<audio controls src="@audio.Url()"></audio>
}
Url()resolves a public-facing URL regardless of source - for a "bynder" DOCUMENT/AUDIO pick this prefers the asset's own original file (BynderAsset.OriginalUrl) over an image-oriented derivative, since DOCUMENT/AUDIO typically has no derivative to fall back to.CanEmbedPlayeristruefor a native Media pick, and for a "bynder" pick only once the asset is confirmed Public in Bynder - a non-Public asset's original file needs an authenticated Bynder session, soUrl()still returns a value but it isn't safe to embed directly (link out to Bynder instead). This is a property for the template to check itself -RenderBynderOrMediaAsset/RenderBynderAssetdon't consult it automatically.- Also implements the Delivery API, same idea as
BynderOrMediaFilebelow - but with a narrowerIApiMedia.Propertiesshape for a "bynder"-sourced item: onlyalt/copyright(no arbitrary mapped-field dictionary), andWidth/Height/Bytesare alwaysnull- the Asset Picker resolves a named derivative rather than one specific output file with known dimensions, unlike the File Picker.
Bynder File Picker values render the same way, minus the derivative name - the editor already chose the output when picking. The picker's own resolved model, BynderOrMediaFile, is one object regardless of whether the editor picked a Bynder asset or a native Umbraco Media item for that property (Source is "bynder" or "media", and every member already switches on it) - a template never needs to branch on where the file came from:
@{
var file = Model.Value<BynderOrMediaFile>("printReadyImage");
}
@Html.RenderBynderOrMediaFile(file, cssClass: "print-image")
BynderOrMediaFile only carries what every kind of picked file genuinely has - FileSize, Url(), and Value<T>(alias) for reading any other property by alias (a native Media property via IPublishedElement.Value<T>, or for a Bynder pick, any Umbraco field currently mapped in Settings > Bynder > Configuration's "Meta Data" box, e.g. file.Value<string>("copyright") - there's no dedicated Caption property, since not every file has one). When the picked file is actually an image (Bynder asset type IMAGE, or a native Media item of the Image media type), it resolves to BynderOrMediaImage instead - a subtype adding Width, Height and resolved Alt text:
@{
var image = Model.Value<BynderOrMediaFile>("printReadyImage") as BynderOrMediaImage;
}
@if (!string.IsNullOrWhiteSpace(image?.Url()))
{
string? caption = image.Value<string>("copyright");
<img class="print-image" src="@image.Url()" alt="@image.Alt" loading="lazy" width="@image.Width" height="@image.Height" />
@if (!string.IsNullOrWhiteSpace(caption))
{
<figcaption class="print-caption">@caption</figcaption>
}
}
Altresolves the field configured in Settings > Bynder > Configuration's "Meta Data" box for a Bynder pick, or the equivalent native Media property for a Media pick - either way, set once when the value is resolved, not lazily per read.Width/Heightare the picked output's own dimensions either way.- CSS classes and the
loadingattribute aren't part of the model at all - a template writes them directly in its own markup, since they're a per-usage rendering concern, not data about the picked file (e.g. a carousel's first slide is worthloading="eager", every other imageloading="lazy"). BynderOrMediaImage.FromMedia(mediaWithCrops)lets a plain native Media Picker property pointed at an image join the same path, without the Bynder File Picker at all.- This property also implements Umbraco's Delivery API - a headless request resolves it to the same
IApiMediashape (url/width/height/bytes/properties) whether the editor picked a Bynder asset or a native Media item, using Umbraco's ownIApiMediaBuilderfor a Media pick and an equivalent synthetic object for a Bynder pick. For an image, resolved alt text is included asproperties["alt"], under that same stable key regardless of source.
Asset Tracking
An optional capability (off by default) that reports which pages use which Bynder assets - both to Bynder itself, via its Asset Usage API, so Bynder's own asset detail view shows "used on" pages, and in this package's own Settings > Bynder dashboard/per-page "Bynder" content tab, so editors can see which of a page's assets are tracked, untrack one, or resync a whole site.
It uses an OAuth2 app of its own (Client Credentials grant, scoped to asset.usage:read/asset.usage:write only - confirmed working), configured either via Bynder:AssetTracking:ClientId/Bynder:AssetTracking:ClientSecret in appsettings.json, or from Settings > Bynder > Configuration in the backoffice (where a client secret is encrypted at rest, never shown again once saved). Leaving both empty - the default - disables it entirely; nothing else in this package requires it.
A background job reconciles tracked assets automatically whenever content referencing Bynder assets is saved, moved to the recycle bin, or deleted - no editor action needed beyond a normal save. See this repository's own Bynder/documentation/asset-tracking-plan.md for the full design.
Meta Data Sync
An optional capability (off by default) that keeps a picked Bynder asset's alt text/copyright in sync with its current Bynder metadata (Name/Description/Copyright), without needing a content republish - a background job polls Bynder periodically and updates a small cache the pickers/RTE embeds already resolve from at render time. It shares the same OAuth2 app/credentials as Asset Tracking above, toggled independently via Bynder:OAuth:MetaDataSyncEnabled (or the "Enable Meta Data Sync" checkbox in Settings > Bynder > Configuration).
Which Bynder field feeds which media type's field is set in that same Settings box's "Meta Data" section - editable per media type in the backoffice, or locked per media type via Bynder:MetaDataFieldMapping in appsettings.json. An editor's own manual override always wins over the synced value.
This capability's underlying Bynder API call needs the shared OAuth2 app's Bynder-side scope extended beyond asset.usage:read/asset.usage:write to also include asset:read. That's a Bynder-portal-side change, not fixable in code, and not yet verified against a real portal - see Services/BynderApiClient.cs's own GetAssetDetailsAsync doc comment.
Transcript Sync
An optional capability (off by default) that automatically fetches a VIDEO/AUDIO Bynder asset's Speech-to-Text transcript, when Bynder has already generated one, and turns it into a ready-to-use WebVTT subtitle track - so a picked video/audio asset can offer captions without an editor separately downloading and re-uploading a subtitle file. It's toggled via Bynder:OAuth:TranscriptSyncEnabled (or the equivalent Settings > Bynder > Configuration checkbox), requires the same OAuth2 credentials as Meta Data Sync, and calls an endpoint Bynder hasn't formally documented - confirmed working against a real portal, but kept an explicit opt-in for that reason.
Once enabled, syncing itself is fully automatic. A picked video/audio BynderFile's SubtitleVttUrl/SubtitleLanguageCode/SubtitleLanguageText populate once Bynder has generated a transcript, and BynderOrMediaFile.ResolveSubtitle(explicitSubtitleFile) resolves which subtitle track to actually use - an editor's own explicitly-picked subtitle file (a separate File Picker pick) always wins over the auto-synced one:
@{
var video = Model.Value<BynderOrMediaFile>("video");
var explicitSubtitle = Model.Value<BynderOrMediaFile>("subtitleFile");
var subtitle = video?.ResolveSubtitle(explicitSubtitle);
}
<video controls src="@video?.Url()">
@if (subtitle is not null)
{
<track kind="subtitles" src="@subtitle.Value.Url" srclang="@subtitle.Value.LanguageCode" label="@subtitle.Value.LanguageLabel" default />
}
</video>
Html.RenderBynderFile(...)/Html.RenderBynderOrMediaFile(...) render a plain <video> with no <track> element - a consuming site needs the Razor above (or equivalent) to actually surface captions; the built-in helpers don't do this automatically.
Logging
This package logs through Umbraco's own logging platform (ILogger<T>/Serilog) - no separate logging setup is needed. Every class that logs lives under the Uxbee.Umbraco.Bynder namespace, so its log events all share that Serilog SourceContext, and installing the package automatically adds a "Find all Bynder log entries" saved search to the backoffice Log Viewer (Settings > Log Viewer > Saved searches) that filters to exactly those events - the same idea as uSync's own saved search, and nothing to configure by hand.
What's logged, at a glance:
- Warning - a Bynder API call failed (including a missing/misconfigured OAuth 2.0 Client ID/Secret), or a stored client secret couldn't be decrypted.
- Information - an admin changed Bynder settings (portal domain, OAuth credentials, field mapping) or ran a connection test, an OAuth2 access token was acquired, or an on-demand/background sync actually changed something (assets tracked/untracked, cache purged).
- The OAuth 2.0 client secret itself is never logged, in any form - only whether one is currently set and what changed about it (added/cleared/unchanged).
Support
- Issues: support.bynder@uxbee.nl
- Documentation: https://docs.bynder.integrationlayer.io/umbraco/
License
See LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Http (>= 10.0.10)
- Microsoft.OpenApi (>= 2.11.0)
- NPoco (>= 6.2.0)
- System.Security.Cryptography.Xml (>= 10.0.10)
- Umbraco.Cms.Api.Management (>= 17.0.0 && < 18.0.0)
- Umbraco.Cms.Core (>= 17.0.0 && < 18.0.0)
- Umbraco.Cms.Infrastructure (>= 17.0.0 && < 18.0.0)
- Umbraco.Cms.Web.Common (>= 17.0.0 && < 18.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 17.0.0 | 34 | 10/14/2026 |