Info Handler
Called after successful login. Returns adapter metadata and optional pre-configuration that shapes the plugin UI for this connection.
info?: (locals: Locals) => HandlerResult<ProviderInfoResponse>CI HUB calls info once after a successful login (and after checkToken on reconnect). The response configures filters, locale pickers, upload options, and other UI elements for the duration of the session.
Response
Return via sendResult:
return sendResult(null, {
remoteSystemPrefix: 'dam.example.com',
// all other fields optional
})remoteSystemPrefix (required)
Base URL hostname of the remote system. CI HUB uses this to build "open in source system" links on assets. Typically derived from the user's connection endpoint:
const remoteSystemPrefix = (locals: Locals) =>
new URL(locals.adapterData.resourceEndpoint).hostnamefilters
Pre-search filters — dropdown menus shown in the plugin UI before the user triggers a search. Set once at login, not updatable during search. Use only for always-applicable filters like "Library" or "Search Operation".
Each filter has an id, name, and options array. Option IDs use the filterId:value convention:
const filters = [
{
id: 'library',
name: 'Library',
options: [
{ id: 'library:all', name: 'All Libraries' },
{ id: 'library:123', name: 'Brand Assets' },
{ id: 'library:456', name: 'Marketing' },
]
}
]Options support default: true to pre-select, isDisabled to gray out, and isActive for toggling. Supports i18nName for localized display names.
These filters are static for the session. For filters that change based on search results (facets), use dynamic filters in the search handler.
dataLocales
Locale picker options shown in the plugin UI. Each locale has an id, optional name/displayName, and an optional default flag:
const dataLocales = [
{ id: 'en', name: 'en', default: true },
{ id: 'de', name: 'de' },
{ id: 'fr', name: 'fr' },
]The selected locale is passed to subsequent handler calls (e.g. search) so the adapter can return localized metadata.
createAssetOptions / updateAssetOptions
Extra form fields shown in the plugin UI during upload (create) or re-upload (update). Each option defines an input with type select, string, or date:
const createAssetOptions = [
{
id: 'metadataTemplateId',
name: 'Metadata Template',
options: [
{ id: 'metadataTemplateId:tmpl_1', name: 'Product Photography' },
{ id: 'metadataTemplateId:tmpl_2', name: 'Marketing Campaign' },
]
},
{
id: 'securityTemplateId',
name: 'Security Template',
options: [
{ id: 'securityTemplateId:sec_1', name: 'Internal Only' },
{ id: 'securityTemplateId:sec_2', name: 'Public' },
]
}
]The user's selection is passed to the createAsset/updateAsset handler in requestData.body.
nativeFolderOrder
If true, CI HUB displays folders in the order returned by the provider API instead of sorting alphabetically. Use when the source system has meaningful ordering (custom sort, priority-based).
customMetadata
Metadata field definitions used for detail panels and filter configurations. Contains groups (grouping headers) and fields (individual metadata fields, optionally assigned to a group via groupId).
See Custom Metadata for full details.
searchConfigs
Multiple search mode configurations. Adds configuration dropdowns to the search UI. searchQueryRequired: true prevents searching without a query string.
const searchConfigs = {
searchQueryRequired: false,
configs: [
{
id: 'single:searchMode',
name: 'Search Mode',
i18nName: { en: 'Search Mode' },
type: 'select',
options: [
{ id: 'all', name: 'All', isActive: false },
{ id: 'any', name: 'Any', isActive: false },
{ id: 'exact', name: 'Exact', isActive: false },
{ id: 'default', name: 'Default', isActive: true },
]
}
]
}requestHeaders
Custom HTTP headers the CI HUB client sends when making direct requests to asset URLs (thumbnails, downloads). Use when the provider requires authentication or cache-control headers on asset URLs:
requestHeaders: {
'X-AdmiralCloud-ClientId': 'your-client-key',
'Cache-Control': 'no-cache'
}transformation
Transformation config for adapters that support on-the-fly asset transformations (e.g. Cloudinary). Contains URLs for fetching available transformations and executing them:
const transformation = {
availableTransformationsUrl: `${adapterBaseUrl}/getAvailableTransformations`,
actionUrl: `${adapterBaseUrl}/doTransformation`
}showPdfPreviewOptions
If true, shows PDF preview options in the plugin UI.
assetSearchHelpUrl
URL linking to external documentation about the provider's search syntax. Shown as a help link in the search UI.
Patterns
Example - Filters from API data
Fetches brand libraries from the API and builds a pre-search filter:
info: async (locals) => {
try {
const { brandId } = locals.adapterData
const libraries = await callApi(locals, { query: queries.getGetBrandLibrariesQuery, variables: { brandId, page: 1, limit: 100 } })
const libraryFilter = {
id: 'library',
name: 'Library',
options: [{ id: 'library:all', name: 'All Libraries' }].concat(libraries.data.brand.libraries.items.map((library: any) => ({ id: `library:${library.id}`, name: library.name })))
}
return sendResult(null, { remoteSystemPrefix: remoteSystemPrefix(locals), filters: [ libraryFilter ] })
} catch (error) {
return sendError(`Error getting info: ${error}`, 400)
}
}Example - createAssetOptions from metadata templates
Fetches metadata and security templates from the API, builds upload form options dynamically:
info: async (locals) => {
try {
const createAssetOptions = []
const filters = [
{
id: 'searchOperation',
name: 'Operation',
options: [
{ id: 'operation:AND', name: 'AND' },
{ id: 'operation:OR', name: 'OR' }
]
},
{
id: 'searchField',
name: 'Search Field',
options: [
{ id: 'keywordSearchField:all', name: 'Everything', default: true },
{ id: 'keywordSearchField:filename', name: 'File Name' },
{ id: 'keywordSearchField:assetId', name: 'Asset ID' },
{ id: 'keywordSearchField:metadata', name: 'Metadata' },
{ id: 'keywordSearchField:content', name: 'Content' }
]
}
]
const metadata = await callApi(locals, 'GET', '/v1/metadata/template')
const security = await callApi(locals, 'GET', '/v1/security/template')
const optionalMetadata = metadata.filter(templateTypeFilter).filter(nonRequired)
if (optionalMetadata.length > 0) {
createAssetOptions.push({
id: 'metadataTemplateId',
name: 'Metadata Template',
options: optionalMetadata.map(item => ({
id: `metadataTemplateId:${item.templateId}`,
name: item.templateName
}))
})
}
if (security?.length > 0) {
createAssetOptions.push({
id: 'securityTemplateId',
name: 'Security Template',
options: security?.map(item => ({
id: `securityTemplateId:${item.templateId}`,
name: item.templateName
}))
})
}
return sendResult(null, { remoteSystemPrefix: remoteSystemPrefix(locals), filters, createAssetOptions })
} catch (error) {
return sendError(error, 400)
}
}Example - dataLocales and customMetadata
Fetches available locales from the API and builds custom metadata field definitions:
info: async (locals) => {
let customMetadata
let dataLocales
const availableLocales = await getAvailableLocales(locals, locals.authPayload)
dataLocales = availableLocales.supportedLocals.
split(',').
map(locale => locale.trim()).
map(locale => ({
id: locale,
name: locale,
default: locale === availableLocales.defaultLocale
}))
try {
customMetadata = { fields: [], groups: [] }
const uiLocale = await getUiLocale(locals, locals.authPayload)
const customFieldClient = await createSoapClient(locals && locals.authPayload, 'apiCustomFieldService')
const customFieldsMapping = await getCustomFieldMapping(customFieldClient)
customMetadata.fields = [
{ id: 'assetType', name: translate('File Category', uiLocale) },
{ id: 'mediaTypeName', name: translate('Asset Type', uiLocale) },
{ id: 'assetAvailability', name: translate('Asset Availability', uiLocale) }
]
customFieldsMapping.forEach(customFieldMap => {
const { id: groupId, name: groupName } = customFieldMap.attributes
customMetadata.groups.push({ id: groupId, name: groupName })
customFieldMap.CustomField.forEach(customField => {
const fieldId = customField.attributes.id
const fieldName = getCustomFieldName(customFieldsMapping, fieldId, uiLocale)
customMetadata.fields.push({ id: `${fieldId}`, name: fieldName, groupId })
})
})
} catch {
// Suppress errors — custom metadata is optional
}
return sendResult(null, {
remoteSystemPrefix: remoteSystemPrefix(locals.authPayload),
showPdfPreviewOptions: true,
dataLocales,
customMetadata
})
}UI Behavior
| Field | Plugin UI Effect |
|---|---|
remoteSystemPrefix | "Open in source" links on assets |
filters | Dropdown menus above search results (static for session) |
dataLocales | Language picker in the plugin toolbar |
createAssetOptions | Extra form fields in the upload dialog |
updateAssetOptions | Extra form fields in the re-upload dialog |
nativeFolderOrder | Folders keep provider ordering instead of A–Z sort |
customMetadata | Metadata panels on asset details, filter options |
searchConfigs | Configuration dropdowns in the search bar |
requestHeaders | Sent with every direct asset URL request |
transformation | Enables transformation UI (Cloudinary) |
showPdfPreviewOptions | Shows PDF preview toggles |