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.
| Parameter | Type | Description |
|---|---|---|
serverUrl | string | The URL to normalize |
originOnly | boolean | If true, returns only the origin (protocol + hostname), stripping path/query/hash |
dontExcept | boolean | If 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 inputCapabilities
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:
| Type | Description |
|---|---|
IntegrationTypeConfig | Shape 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 |
DahTypeConfig | Generic 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
| Export | Module | Common use |
|---|---|---|
fs | node:fs | fs.readFileSync — loading logo/glyph images |
path | node:path | path.join(import.meta.dirname, 'logo.png') |
crypto | node:crypto | crypto.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 getmimeTypes
MIME type lookup from mime-types.
mimeTypes.lookup('photo.jpg') // → 'image/jpeg'
mimeTypes.extension('image/jpeg') // → 'jpeg'
mimeTypes.lookup(filename) || 'application/octet-stream' // safe fallbackoauth2
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.