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. For the CLI commands that use this package (dev, config, submit), see the CLI reference.

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<TConfig>(config)

The entry point for every integration. 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. Pass an optional generic TConfig to type locals.adapterData, per-handler requestData, and OAuth state fields instead of using manual casts. See Typed Integrations for the full guide.

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

With a type config:

export default defineIntegration<MyIntegrationConfig>({
  name: 'My DAM',
  // ... handlers — locals.adapterData and requestData are typed from MyIntegrationConfig
})

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<TConfig>(error?, token?, expiresIn?, refreshToken?, refreshExpiresIn?, adapterData?)

Auth handlers only. Stores the session or reports an auth error. Pass the same type config as defineIntegration to type the adapterData argument. See Auth helpers.

// 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 integration 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<TConfig>(state, data)

Stores arbitrary data against a state token. Pass the same type config as defineIntegration to type data from login.oauthState. See Auth helpers.

await setStateAdapterDataAsync(state, { serverUrl, verifier })

getStateAdapterDataAsync<TConfig>(state)

Retrieves data stored for a state token. Pass the same type config as defineIntegration to type the return value. See Auth helpers.

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

For stricter compile-time types on adapterData, requestData, and auth state, pass a type config to defineIntegration and related helpers. See Typed Integrations for the full guide, examples, and exported type reference.

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.

Type configuration exports

These types support the generic on defineIntegration, auth helpers, and DAH:

TypeDescription
IntegrationTypeConfigShape of the generic argument
IntegrationDefinition<TConfig>Full integration object type for a given config
AdapterDataFromConfig<TConfig>adapterData shape for sendAccessToken and merged locals
OAuthStateFromConfig<TConfig>login.oauthState shape for state helpers
DahTypeConfigGeneric config for DAH (adapterData, data)
DahFunction<TConfig>Typed DAH handler
DirectAccessHelper<TConfig>Object exported from dah.ts

See Exported types for the complete list.


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