CI HUBCI HUB SDK
Reference

SDK Packages & Exports

Everything you need is available from @ci-hub/integration-sdk. You don't need to install Node.js built-ins or most third-party libraries separately.

import {
  // Integration
  defineIntegration,

  // Response helpers
  send, sendResult, sendError, sendStatus, sendAccessToken, sendRedirectUri,

  // Auth state helpers
  getStateCodeAsync, getStateAdapterDataAsync, setStateAdapterDataAsync,

  // Rendering
  render, ensureHttps,

  // Capabilities
  defaultFolderCapabilities, defaultAssetCapabilities,

  // Enums
  CategoryEnum, HashAlgorithmEnum, FieldTypeEnum,

  // Node.js built-ins
  fs, path, crypto,

  // Third-party
  axios, config, mimeTypes, oauth2,
} from '@ci-hub/integration-sdk'

Import only what you use.


Integration

defineIntegration(config)

The entry point for every adapter. Accepts a configuration object with metadata and all handler functions. Returns the integration definition.

All handler signatures are fully typed — the SDK infers the correct locals and requestData types for each handler, so you don't need to annotate them manually.

export default defineIntegration({
  name:         'My DAM',
  version:      '1.0.0',
  logo,
  glyph,
  contact,
  capabilities: { /* ... */ },
  login:        async (locals, requestData) => { /* ... */ },
  // ... other handlers
})

Response helpers

send(data)

Successful response with a payload. Use for all content handlers that return data.

return send({ assets: [], folders: [], filters: [], more: undefined })

sendResult(error, data)

Explicit null-error form of send. Pass null as the first argument on success.

return sendResult(null, { filters: [] })

sendError(message, statusCode)

Handler failed. The message is shown to the user in the CI HUB UI.

return sendError(`Error searching assets: ${error}`, 400)

sendStatus(code)

Success with no response body. Use for deleteAsset, deleteFolder, logout.

return sendStatus(200)

sendAccessToken(error?, token?, expiresIn?, refreshToken?, refreshExpiresIn?, adapterData?)

Auth handlers only. Stores the session or reports an auth error.

// Success
return sendAccessToken(null, accessToken, expiresIn, refreshToken, undefined, {
  resourceEndpoint: serverUrl,
  orgId: selectedOrg,
})

// Failure
return sendAccessToken('Invalid credentials. Please try again.')

// Clean abort (user canceled)
return sendAccessToken()

sendRedirectUri(url)

Redirects the user to a URL. Used during OAuth flows to redirect to the authorization endpoint, or to redirect back to the adapter endpoint to initialize state.

return sendRedirectUri(authorizationUrl)

Auth state helpers

These persist data between redirect steps in multi-phase login flows. Data is stored server-side only.

getStateCodeAsync()

Generates an opaque, cryptographically random state token.

const state = await getStateCodeAsync()

setStateAdapterDataAsync(state, data)

Stores arbitrary data against a state token.

await setStateAdapterDataAsync(state, { serverUrl, verifier })

getStateAdapterDataAsync(state)

Retrieves data stored for a state token.

const { serverUrl, verifier } = await getStateAdapterDataAsync(state)

Rendering

render(templatePath, data)

Renders an .ect HTML template with the provided data. Returns the rendered HTML as a response. The .ect extension is added automatically.

return render(path.join(import.meta.dirname, 'login'), {
  action:  locals.endpointUrl,
  logo,
  contact: config.get('myIntegration.contact'),
})

ensureHttps(serverUrl, originOnly, dontExcept)

Normalizes a URL to HTTPS. Use on all user-provided server URLs before storing or calling them.

ParameterTypeDescription
serverUrlstringThe URL to normalize
originOnlybooleanIf true, returns only the origin (protocol + hostname), stripping path/query/hash
dontExceptbooleanIf true, returns empty string on invalid URL instead of throwing
const normalized = ensureHttps(userUrl, true, true)
// 'https://example.com' — origin only, no throw on bad input

Capabilities

defaultFolderCapabilities

Default capability object for folders. All destructive flags default to false. Spread this first, then override:

const caps = { ...defaultFolderCapabilities, canAddAsset: true, canAddFolder: true }

defaultAssetCapabilities

Default capability object for assets. Spread this first, then override with values derived from your system's permission data:

const caps = { ...defaultAssetCapabilities, canUpdateAsset: item.permissions.canEdit }

Types

Locals

Type for the first argument of every handler. Inferred automatically inside defineIntegration — only import it when you need to type a helper function outside the integration definition:

import type { Locals } from '@ci-hub/integration-sdk'

async function callApi(locals: Locals, endpoint: string) { /* ... */ }

TypedRequestData<Q, B, P>

Generic type for the second argument of handlers. Each handler inside defineIntegration gets a specialized version automatically (e.g., TypedRequestData<SearchQuery, SearchBody> for search).

Import this when you need to type a helper that receives request data:

import type { TypedRequestData, SearchQuery, SearchBody } from '@ci-hub/integration-sdk'

function parseSearchParams(requestData: TypedRequestData<SearchQuery, SearchBody>) {
  const query = requestData.query.query || ''
  // ...
}

IntegrationInfo

Type for the return value of the info handler.


Enums

CategoryEnum

Integration category. Use in capabilities.category:

capabilities: { category: CategoryEnum.DAM }

HashAlgorithmEnum

Asset deduplication strategy. Use in capabilities.assetHashAlgorithm:

capabilities: { assetHashAlgorithm: HashAlgorithmEnum.FILE_ATTRIBUTES }

FieldTypeEnum

Custom metadata field types. Use in checkToken values and custom metadata declarations:

values: [{ id: 'email', name: 'Email', value: user.email, type: FieldTypeEnum.TEXT }]

Node.js built-ins

ExportModuleCommon use
fsnode:fsfs.readFileSync — loading logo/glyph images
pathnode:pathpath.join(import.meta.dirname, 'logo.png')
cryptonode:cryptocrypto.randomBytes, crypto.createHash — PKCE helpers

Third-party libraries

axios

Pre-configured Axios instance for HTTP requests.

const { data } = await axios.get(url, { headers, params })

const { data } = await axios.post(url, body, { headers })

await axios.put(uploadUrl, buffer, {
  headers: { 'Content-Type': mimeType },
  maxContentLength: Infinity,
  maxBodyLength:    Infinity,
})

config

Typed config reader backed by node-config.

config.has('myIntegration.clientId')        // boolean — always check optional keys
config.get('myIntegration.clientId')        // string | number | object — throws if missing
config.get<number>('myIntegration.limit')   // typed get

mimeTypes

MIME type lookup from mime-types.

mimeTypes.lookup('photo.jpg')          // → 'image/jpeg'
mimeTypes.extension('image/jpeg')      // → 'jpeg'
mimeTypes.lookup(filename) || 'application/octet-stream'  // safe fallback

oauth2

OAuth 2.0 client from simple-oauth2. Used to implement authorization code + PKCE flows.

const client = new oauth2.AuthorizationCode({
  client: { id: 'clientId', secret: '' },
  auth: { tokenHost, tokenPath, authorizePath, revokePath },
  options: { bodyFormat: 'form' },
})

const authUrl = client.authorizeURL({ redirect_uri, scope, state, code_challenge })

const { token } = await client.getToken({ code, redirect_uri, code_verifier })

const existing    = client.createToken({ refresh_token })
const { token: t} = await existing.refresh({ grant_type: 'refresh_token' })

const t = client.createToken({ access_token, refresh_token })
await t.revokeAll()

See Login Flow → for the full OAuth 2.0 + PKCE implementation.

On this page