ccs-files-client changelog
Versions follow semver. For the package itself see ccs-files-client. The reference is at /sdks/ccs-files-client/reference/.
2.0.0 (2026-10-25)
Section titled “2.0.0 (2026-10-25)”The File Service’s address is two variables, FILES_API_URL and FILES_API_RESOURCE, the same shape as every CCS
service.
Changed
Section titled “Changed”- Default endpoints, per value: the option (
apiUrl/resource) >FILES_API_URL/FILES_API_RESOURCE> theCCS_ENVIRONMENTpreset (local|staging|production, default production). When only one of the two is given (option or variable), the other is derived from it if it has the standard shape (…/api/v1↔…/api), else it is the preset’s; a loneapiUrloption now carries itsresourcewith it. Each value must be an absolute http(s) URL: a malformed one (options included) throws at construction, naming it. filesEndpoints({ apiUrl?, resource?, env? })returns{ apiUrl, resource }.
Removed
Section titled “Removed”- The single File Service origin variable of 1.4.0, the
originoption offilesEndpoints()andFilesEndpoints.origin: setFILES_API_URL+FILES_API_RESOURCE(orCCS_ENVIRONMENT). FilesEndpoints.deprecation.FileLock.user_id, which the service never sent; the holder isholder_user_id.
1.4.0 (2026-10-25)
Section titled “1.4.0 (2026-10-25)”Default endpoints. Source-compatible with 1.3.0: every option you pass today keeps its meaning.
issuerUrl,apiUrlandresourceare optional. Omitted, they are the production Identity Server and File Service, unless the environment says otherwise:CCS_ENVIRONMENT(local|staging|production), a File Service origin variable (API<origin>/api/v1, resource<origin>/api; replaced in 2.0.0 byFILES_API_URL+FILES_API_RESOURCE),CCS_ISSUER_URL. The package still has no runtime dependencies.FILES_API_URL+FILES_API_RESOURCEwere honoured when both were set and agreed. An explicit option always wins.FilesClientOptions.env: where those variables are read. Defaultprocess.env;{}ignores the environment.FilesClient.endpoints: the{ issuerUrl, apiUrl, resource }the client uses, after the defaults.- The addresses themselves, exported from the package root:
FILES_ORIGINSandIDENTITY_ORIGINS(production,staging,local),FILES_AUDIENCE(files-api),filesEndpoints({ origin?, env? })→{ origin, apiUrl, resource, deprecation }(precedence:origin> the origin variable > theFILES_API_URL+FILES_API_RESOURCEpair >CCS_ENVIRONMENT> production; a malformed origin, an unknownCCS_ENVIRONMENTor an inconsistent pair throws) anddefaultIssuerUrl(env?)(CCS_ISSUER_URL, else the preset). Neither readsprocess.envitself: passenv. A backend that only needs the File Service’sresourceorapiUrl(a token route, a consumer configuration) callsfilesEndpoints({ env: process.env }).
Changed
Section titled “Changed”- A blank
issuerUrl,apiUrlorresource(an empty string) still throws, now with “must not be blank (omit it for the platform default)”; only an omitted (undefined) field takes the default. A malformed origin variable orCCS_ENVIRONMENTthrows at construction.
1.3.0 (2026-10-24)
Section titled “1.3.0 (2026-10-24)”Application-managed folders, as the File Service now serves them. Source-compatible with 1.2.0.
- Application-managed folders.
folders.create({ ..., managed: true })creates a folder already managed by your application,folders.update(folderId, { managed })marks (true) or unmarks (false) one (managedonCreateFolderInputandUpdateFolderInput; machine clients withfiles:manageonly, never the root). For people a managed folder and its subtree are read-only; your machine clients change them as before. Auditedfolder.managedandfolder.unmanaged. FileandFoldergainmanaged(truefor the marked folder and everything below it) andmanaged_by(ManagedBy:{ client_id, name, managed_folder_id, managed_at },nullwhen not managed; the nearest marked folder with nested marks).- Errors.
folder_managed(403, aFilesForbiddenErrorwithbody.managed_folder_id): what people cannot do inside a managed subtree, share links included (your machine client still creates them).FilesForbiddenError.managedFolderIdreads the managing folder id (nullon other codes) andisFolderManagedError(error)narrows to it.principal_not_allowed(403) also answers a user token sendingmanaged.
1.2.0 (2026-10-23)
Section titled “1.2.0 (2026-10-23)”Legal hold, usage trends and platform administration, as the File Service now serves them. Source-compatible with 1.1.0.
- Legal hold.
files.hold.place(fileId, { reason, until?, acting_user_id? })(PUT P/files/:file_id/hold: places a hold, or changes the reason and the end of the one in place),files.hold.release(fileId, { reason?, acting_user_id? })(DELETE P/files/:file_id/hold, a body only when a field is given) andholds.list({ placed_by?, after?, limit? })(GET P/holds: every held file of the application, newest hold first). They needfiles:manage.FileHoldgainsuntil(null: until released);PlaceHoldInput,ReleaseHoldInput,HoldsQuery. - Usage trend.
usage()typesdaily(DailyUsage[]: the last 30 days, oldest first,{ date, bytes, rendition_bytes, trashed_bytes, files, versions, quota_bytes }; a day without a snapshot is absent, never zero). - Platform administration (
client.platform, no context; only for a client of the Files platform application itself withfiles:config.manage, every other client answers 403platform_admin_required):tenants,tenant,setTenantSettings,defaults,setDefaults; usage trendsusage({ days, sort, limit })andtenantUsage(tenantId, { days }); a tenant’s audit log across its applicationstenantAudit(tenantId, query)(the filters ofaudit.listplusapplication_id); the exportsexportTenantAudit(tenantId, { ...filters, format })andexportTenantUsage(tenantId, { from, to, format }), which resolve with aFilesExport(content_type,filename,truncated,body,text(),lines()); storagestorage.tenant(tenantId),storage.assign(tenantId, { storage_account_id }),storage.accounts(list,create,get,update,delete,verify) andstorage.migrations(start,list,get), with their types (PlatformTenant,PlatformUsageTrends,PlatformTenantUsageTrend,TenantSettings,StorageAccount,TenantStorage,StorageMigration, …). Creating an account and starting a move are never retried;storage.accounts.verifyis retried like a read. - Errors.
file_on_hold(409, aFilesConflictErrorwithbody.hold) answersfiles.purgeandfiles.versions.deletewhile a hold is in force;platform_admin_required(403);storage_account_not_foundandstorage_migration_not_found(404);storage_account_in_use,storage_account_not_verifiedandstorage_migration_in_progress(409);storage_account_verification_failedandstorage_adapter_not_allowed(422). AuditRow.target.typeincludestenant,platform,taxonomyandtrash.
Changed
Section titled “Changed”- Search wording.
SearchQuery.in:namematches names that start with the query, case-insensitively (a prefix, not a substring:IMGfindsIMG_5913.jpg,5913does not); titles and other words match whole words throughkeywords; with the default['name', 'keywords'], a query no whole word matches falls back to the name prefix.SearchResult.totalis exact up to 10,000 andnullabove. - URLs: treat
public_urland every signed URL as opaque. A file’spublic_urlcan change when its tenant’s files move to another storage; read it from the file again rather than keeping it for good.
1.1.0 (2026-10-23)
Section titled “1.1.0 (2026-10-23)”Processing, content search and webhooks, as the File Service now serves them, and the response types brought in line with the service’s OpenAPI document. Source-compatible with 1.0.0 except where a 1.0.0 type described a field the service never sent (listed under Fixes).
- Processing.
ScanStatus(pending,clean,infected,skipped,failed) and the documented meaning ofprocessing.scanandprocessing.text;RenditionStatus(pending,ready,skipped,failed); renditionsmp4_720pandmp4_1080pwithduration_ms, anderroron askippedorfailedrendition (source_smaller,duration_exceeds_limit,size_exceeds_limit,duration_unknown);files.renditions.regeneratetakesRenditionNames.FileMediatypesmedia:captured_atandcamerafor images,titleandauthorfor PDFs, next to the dimensions, duration, codec, rotation and page count. - Content search.
SearchQuery.indocumentscontent;SearchHighlight({ field, snippet }) onSearchHit. - Webhooks.
WebhookConfiggainssecret_set,consecutive_failures,last_delivery_at,disabled_atandupdated_at;WebhookDeliverygainsevent_id,error,duration_msandnext_attempt_at, withWebhookDeliveryStatus(delivered,retrying,failed).webhooks.setdocuments 400webhook_url_not_allowed(aFilesApiError) and 422webhook_unreachable. - Settings.
scan_policy,scan_failed_policy,scan_oversize_policyandtext_extraction_enabledare served and documented onSettingsandSettingsUpdate. CreatedShare(whatshares.createresolves with:tokenandurlare required there),TaxonomyInput(whattaxonomies.setsends),EntryPrincipal,EffectiveEntry,FileHold,PreviewKind;trash_reasonon a trashedFile;external_refonUpdateFolderInput;by_kindandby_folderonUsage.
Changed
Section titled “Changed”files.waitUntilReadyresolves when processing ran to its last step (notifydone) even though a step that does not stop processing failed on the way (a rendition, the media metadata, text extraction, or a scan the application lets through underscan_failed_policy: allow); checkprocessingon the file it resolves with. It still rejects withprocessing_failedwhen a step failed and processing stopped there (a scan that failed underscan_failed_policy: block, or a step that ran out of attempts). In 1.0.0 anyfailedstep rejected.
Fixes (types that did not match the wire)
Section titled “Fixes (types that did not match the wire)”Share:target: { type, id },password_protected,downloads,access_count,created_by: CreatedBy,expires_at: string,revoked_atandlast_accessed_atalways present. The 1.0.0 fieldstarget_type,target_idandpassword_requiredwere never sent and are removed.AccessEntry.granted_byis aCreatedBy({ user_id, client_id }), not a string;expires_atis always present; aroleprincipal carries the role’sname. The 1.0.0display_nameon the entry was never sent and is removed.ItemPermissions.effectiveisEffectiveEntry[]({ principal, actions }), notAccessEntry[].EntryRole(permissions.roles()) hasdisplay_name;Capabilities.roles[].role_idmay benullandscopeisTENANTorAPPLICATION.Preview.kindisPreviewKind(image,video,audio,pdf,text,none), not aFileKind; renditionwidthandheightmay benull.UploadTicketStatusincludesfailed;UploadTicket.url,part_size,parts_totalandpartsarenullwhen they do not apply.Folderhasdepth,state(available,trashed) andbusy;created_by,created_atandupdated_atarenullfor the root;metadatais a string map, nevernull.File:customis a string map;mediais nevernull;holdisFileHold | null;lockhaskind,holder_user_id,holder_client_idandnote(it isnulluntil check-out exists).Versionhasstate(available,rejected,quarantined) andsource(upload,restore,edit,copy).RenditionInfoandRendition:statusis aRenditionStatus,mime_type,size,width,heightandduration_msare always present (nullwhen unknown),erroronly on a skipped or failed rendition.Job:progressis{ done, total },erroris{ code, message }, withcreated_atandupdated_at.SearchResult.totalis always present;Taxonomyhasname;AuditRowtenant_id,application_id,ipanduser_agentmay benull;Settings.updated_atisnullbefore the first change.
1.0.0 (2026-10-22)
Section titled “1.0.0 (2026-10-22)”First release: the Node.js backend SDK for the File Service.
FilesClientwithfolders(root,create,get,listByExternalRef,children,path,update,move,copy,delete,restore,usage),uploads(create,status,parts,complete,abort),files(get,listByExternalRef,update,move,copy,delete,restore,purge,downloadUrl,urls,preview,waitUntilReady,effectivePermissions,effectivePermissionsBatch,checkout,checkin,discardCheckout),files.versions(list,create,get,restore,pin,delete),files.renditions(list,regenerate),permissions(get,replace,grant,revoke,roles),search,tags,taxonomies(get,set),shares(create,list,listAll,revoke),trash(list,empty),settings(public,get,update),usage,tenantUsage,audit(list,forFile,forFolder),webhooks(get,set,deliveries),jobs(get,wait) andcapabilities.upload(source, input, { concurrency, onProgress, signal }): ticket, single PUT with exactly the ticket’s headers or multipart with 4 parts in flight, part URLs beyond the first 100 in batches of 100, each PUT retried up to 5 times with full-jitter backoff, one URL refresh on a 403, etags fromETag, completion; the ticket is aborted on give-up (FilesUploadErrorwithpartNumber) and on abort. Sources:Buffer,Uint8Array,Blob,Readable(withinput.size) and{ path }.files.waitUntilReady(fileId, { timeoutMs, pollMs, signal })andjobs.wait(jobId, ...)poll until the file is processed or the job is done.FilesClient.verifyWebhookSignature({ rawBody, signatureHeader, secret, toleranceSeconds, now }):X-Files-Signature(t=…,v1=…, HMAC-SHA256 of"<t>.<raw body>"), constant-time, 300 s tolerance by default.- Request and response types are the service’s snake_case wire format (
File,Folder,Version,UploadRequestwithon_name_conflict,UploadTicket,AccessEntry,Share,Settings,PublicSettings,SearchQuery,SearchResult,Preview,AuditRow,Usage,Job,Page<T>, …); constructor options, the per-callcontext({ tenantId, applicationId }) and the SDK’s option objects are camelCase. client_credentialstokens over HTTP Basic withscope(defaultfiles:manage) andresource, cached untilexp - 60 s, one in-flight token request, one refresh and replay on a 401.- Retries for 5xx (not 501) and network errors (
maxRetries, default 3) with exponential backoff and jitter, on reads and idempotent writes only; the writes that create carry anIdempotency-Keygenerated once and reused across retries;If-Matchthrough{ ifMatch }onfiles.updateandfolders.update. - Typed errors:
FilesApiError(status,code,body,retryAfterSeconds),FilesUnauthorizedError,FilesForbiddenError,FilesNotFoundError,FilesConflictError,FilesPreconditionFailedError,FilesPayloadTooLargeError,FilesUnprocessableError,FilesRateLimitedError,FilesNotImplementedError,FilesContextError,FilesUploadError, andtoFilesApiError. - ESM and CommonJS builds with declarations, zero runtime dependencies, Node.js 20 or newer (built and tested on Node.js 24).