Xeonr Developer Docs
SDK

Client SDK

Browser SDK for auth, upload, download, and built-in pickers.

@xeonr/uploads-sdk provides a browser-first SDK for integrating upl.im into your application. It handles OAuth authentication, file uploads and downloads, optional built-in picker UI, real-time event streaming, and React bindings.

Install

pnpm add @xeonr/uploads-sdk @xeonr/uploads-protocol

Quick start

import { Upl } from '@xeonr/uploads-sdk';

const upl = new Upl({
  clientId: 'your-client-id',
  env: 'prod',
});

// Upload a file
const image = new Blob([/* bytes */], { type: 'image/png' });
const uploaded = await upl.upload(
  {
    filename: 'example.png',
    contentType: 'image/png',
    bucketHint: 'my-bucket',
    path: '/images',
  },
  image,
);

// Download metadata
const resolved = await upl.download({
  bucketHint: 'my-bucket',
  uploadId: uploaded.uploadId,
});

// Download content
const { data } = await upl.downloadContent({
  bucketHint: 'my-bucket',
  uploadId: uploaded.uploadId,
  as: 'blob',
});

Configuration

UplOptions

const upl = new Upl({
  clientId: 'your-client-id',       // Required — OAuth client ID
  env: 'prod',                       // 'prod' | 'stage' | 'dev' (default: 'prod')
  scopes: [                          // OAuth scopes requested during login
    'upl:bucket:read',
    'upl:uploads:create',
    'profile',
  ],
  ui: { disabled: false },           // Disable built-in picker UI
  auth: { /* see Authentication */ },
});
OptionTypeDefaultDescription
clientIdstringOAuth client ID (required)
env'prod' | 'stage' | 'dev''prod'API environment
scopesstring[]['upl:bucket:read', 'upl:uploads:create', 'profile']OAuth scopes
ui.disabledbooleanfalseDisable built-in bucket/folder/file pickers
authUplAuthOptionsAdvanced auth configuration

Environments

EnvAPI endpointAuth endpoint
produploads-api.xeonr.ioauth.xeonr.io
stage / devuploads-api.xeonr.devauth.xeonr.dev

Upload API

const upload = await upl.upload(request, data);

Request fields

FieldTypeDescription
filenamestringDisplay filename (required)
bucketIdstringTarget bucket ID
bucketHintstringFuzzy bucket match (name, alias, or partial)
pathstringDirectory path (default: /)
contentTypestringMIME type
sizenumberFile size in bytes (inferred from Blob if omitted)
slugstringURL-safe slug (defaults to filename)
tagsstring[]Tags for categorisation
pickerConfigUploadPickerConfigPicker UI configuration

Data types

The data parameter accepts Blob, ArrayBuffer, or Uint8Array. Files over 8MB automatically use multipart upload.

Upload flow

  1. Resolve the target bucket (by bucketId, bucketHint, or interactive picker)
  2. Show folder/overwrite picker UI (unless disabled)
  3. Request an upload slot from the API
  4. Upload file content (direct or multipart for large files)
  5. Finalise the upload record

Picker configuration

await upl.upload({
  filename: 'report.pdf',
  bucketHint: 'my-bucket',
  pickerConfig: {
    enabled: true,               // Show folder picker (default: true when UI enabled)
    allowOverwrite: true,        // Allow replacing existing files
    overwriteTarget: 'upl_123', // Pre-select a file to overwrite
    allowFilenameEdit: true,     // Allow editing filename in picker
    allowSlugEdit: false,        // Allow editing slug in picker
    selectionFilter: {
      extensions: ['pdf', 'doc', 'docx'],
      types: [UploadType.DOCUMENT],
    },
  },
}, data);

Download API

Resolve an upload

const upload = await upl.download({
  bucketHint: 'my-bucket',
  uploadId: 'upl_123',
});
FieldTypeDescription
bucketId / bucketHintstringBucket identifier
uploadIdstringDirect upload ID lookup
uploadPathstringFull path lookup
pathstringDirectory filter
filenamestringExact filename filter
typeUploadTypeFilter by file type
tagsstring[]Filter by tags
nonInteractivebooleanThrow on ambiguity instead of showing picker
pickerConfigDownloadPickerConfigPicker UI configuration

If filters match multiple files, the picker UI opens. With nonInteractive: true, ambiguous or no-match cases throw an error.

Download content

const { upload, data } = await upl.downloadContent({
  bucketHint: 'my-bucket',
  uploadId: 'upl_123',
  as: 'blob', // 'blob' | 'arrayBuffer' | 'text' | 'stream'
});

The as parameter controls the return type of data:

ValueReturn type
'blob' (default)Blob
'arrayBuffer'ArrayBuffer
'text'string
'stream'ReadableStream<Uint8Array>

React hooks

import { UplProvider, useUpl } from '@xeonr/uploads-sdk';

function Root() {
  return (
    <UplProvider
      options={{
        clientId: 'your-client-id',
        env: 'prod',
        scopes: ['upl:user:read', 'upl:uploads:create'],
      }}
    >
      <App />
    </UplProvider>
  );
}

function Uploader() {
  const { auth, upload } = useUpl();

  return (
    <div>
      {!auth.isAuthenticated && (
        <button onClick={() => auth.login()}>Login</button>
      )}
      <button
        disabled={upload.loading}
        onClick={async () => {
          const file = new Blob(['hello'], { type: 'text/plain' });
          await upload.run({ filename: 'hello.txt' }, file);
        }}
      >
        Upload
      </button>
    </div>
  );
}

useUpl() return value

const {
  client,          // Upl instance
  auth,            // Auth state and methods
  upload,          // Upload action
  download,        // Download action
  downloadContent, // Download content action
} = useUpl();

Auth state

auth.state          // UplUserState { status, token, user }
auth.loading        // boolean
auth.error          // Error | null
auth.isAuthenticated // boolean

auth.login()        // Start OAuth login
auth.forceLogin()   // Force re-login (clears cached token)
auth.logout()       // Clear token
auth.refresh()      // Refresh auth state

Action hooks

upload, download, and downloadContent share the same interface:

action.run(...)     // Execute the action (same args as the Upl method)
action.loading      // boolean
action.error        // Error | null
action.data         // Result | null
action.reset()      // Clear state

Provider options

<UplProvider
  options={uplOptions}
  authOptions={{
    loadOnMount: true,    // Auto-load auth state on mount (default: true)
    refreshOnFocus: true, // Refresh auth when window regains focus (default: true)
  }}
>

Non-interactive mode

For server-like or headless flows, disable the UI and pass explicit IDs:

const upl = new Upl({
  clientId: 'your-client-id',
  ui: { disabled: true },
});

await upl.download({
  bucketId: 'bucket-id',
  uploadId: 'upload-id',
  nonInteractive: true,
});

In non-interactive mode, missing or ambiguous bucket/upload selection will throw instead of opening a picker.

Script tag usage

For non-module environments, use the embed build:

<script src="https://cdn.example.com/@xeonr/uploads-sdk/embed.js"></script>
<script>
  const upl = new window.Upl({ clientId: 'your-client-id' });
  // or: new window.UplSDK.Upl(...)
</script>

The embed script exposes:

  • window.Upl — the Upl class
  • window.UplUploadType — the UploadType enum
  • window.UplSDK — namespace containing both

On this page