Client library
The official TypeScript/JavaScript SDK. Install, authenticate, connect a DAM, and read assets without touching the wire.
@ci-hub/access-sdk is the official TypeScript/JavaScript client for the Access SDK API. It wraps every currently available endpoint from Getting started behind typed methods, owns the token lifecycle, and resolves download URLs for you. The declared upload() and update() are not implemented yet (see Writing assets). Everything the library does can also be done with plain HTTP; the guides in this section show the wire-level detail, and the library is the shortcut past it.
npm install @ci-hub/access-sdkRuntime support
The core depends only on fetch and runs in modern browsers, Office.js add-ins, Node 20+, Bun, and Deno. It never imports Node built-ins.
The partner-JWT signer needs your private key, so it is server-only and lives on a separate subpath, @ci-hub/access-sdk/node. Keep it out of client bundles: the key must never reach a browser.
Authenticate
On your server, mint the partner JWT described in Partner registration:
import { signPartnerJwt } from '@ci-hub/access-sdk/node'
const partnerJwt = await signPartnerJwt({
privateKeyPem: process.env.PARTNER_PRIVATE_KEY!,
keyId: 'my-kid',
claims: {
iss: 'https://issuer.example.com',
aud: 'https://api.ci-hub.com',
sub: 'user-123',
email: 'jane@customer.example.com',
},
})aud is always https://api.ci-hub.com, on every environment. It identifies CI HUB in your partner registration and does not change with the client's baseUrl.
In your application, exchange it for a CI HUB session (Exchange token) and hand the result to the TokenManager, which refreshes both sessions from then on:
import { CiHubAccessClient, TokenManager } from '@ci-hub/access-sdk'
const client = new CiHubAccessClient({ baseUrl: 'https://stage.ci-hub.com/api/v1' })
const tokens = new TokenManager(client)
const session = await client.exchangeToken({ partnerJwt })
await tokens.setCihubSession(session)When your token carries no email claim, pass the address as emailFallback instead. The constraints on a pseudonymous address are in Email outside the JWT:
const session = await client.exchangeToken({
partnerJwt,
emailFallback: 'u-8f21c4@anon.your-platform.example.com',
})Connect a DAM
The DAM login is a browser redirect plus polling, as described in DAM authentication. The library runs the poll loop for you:
const login = await tokens.withCihubAuth((token) =>
client.beginDamLogin({ provider: 'dropbox', accessToken: token }),
)
// Open login.redirect_uri for the user, then:
const damTokens = await client.waitForDamLogin({ state: login.state })
await tokens.setDamSession('dropbox', damTokens)Providers that would prompt the end user for their DAM instance take the answer up front through providerParams, which removes that page from the flow. The providers that read it are listed in Provider parameters:
const login = await tokens.withCihubAuth((token) =>
client.beginDamLogin({
provider: 'frontify',
accessToken: token,
options: { providerParams: { serverUrl: 'https://acme.frontify.com' } },
}),
)Read assets
Every content call carries the two tokens. withDamAuth supplies fresh ones and retries once on an expired token:
const results = await tokens.withDamAuth('dropbox', (cihubToken, damToken) =>
client.search({ accessToken: cihubToken, damToken, options: { query: 'logo' } }),
)
const first = results.assets[0]
if (first) {
const response = await tokens.withDamAuth('dropbox', (cihubToken, damToken) =>
client.download({ url: first.downloadUrl, damToken, cihubToken }),
)
const bytes = await response.arrayBuffer()
}download resolves the URL placeholders and attaches credentials only where they belong: the CI HUB proxy gets both tokens; provider-direct URLs go straight to the DAM. Folder browsing, similarity search, asset detail, and versions follow the same shape; see the content reference for each endpoint.
Token lifecycle
TokenManager implements the full refresh contract. The CI HUB session refreshes proactively from the server's expires_in; DAM sessions report no lifetime at login and refresh reactively on the first 401. Both keep the old refresh token when a provider omits it, and a provider with no refresh path surfaces as a typed DamReauthRequired: run a fresh DAM login. Storage is pluggable; the default keeps tokens in memory.
Errors
Every failing call throws AccessSdkError carrying the structured error envelope: status, code, source (cihub or integration), message, and optional details and provider.
Writing assets
upload() and update() exist with their final signature and throw NotImplementedError (code cihub-not-implemented) until the write engine ships. Code written against them today keeps compiling when it does.
Types
All request and response types are generated from the same OpenAPI specification that produces this reference, so the library and these pages cannot drift apart. The raw paths, components, and operations types are exported for advanced use.