The Label Studio SDK includes an interface CLI group for building, validating, previewing, and syncing interfaces from local JSX modules.
Interfaces are Enterprise-only in Label Studio. The CLI can still scaffold and validate files locally. Syncing and project creation require an Enterprise instance with the interfaces API enabled. Preview needs an Enterprise instance only to download its version-compatible browser artifact on first use and when revalidating the cache.
- Python package:
label-studio-sdk - Node.js and npm on
PATH - A Label Studio Enterprise URL and API token for server operations
Set the server configuration once per shell:
export LABEL_STUDIO_URL="https://app.humansignal.com"
export LABEL_STUDIO_API_KEY="YOUR_API_KEY"You can pass --lse-url and --token to individual commands instead of using environment variables.
Check local setup:
label-studio-sdk interface doctorThe first validation or doctor run installs the Node validator dependencies into a user cache directory.
Create a starter interface:
label-studio-sdk interface init ./sentiment-interface
cd sentiment-interfaceValidate the interface statically and run its browser scenario:
label-studio-sdk interface validate .Open the live playground and sync local file changes as you edit:
label-studio-sdk interface preview .Pull an existing interface and continue locally:
label-studio-sdk interface pull --id 73 ./existing-interface
cd existing-interfaceCreate or update a saved interface in Label Studio. By default, this creates a local draft version:
label-studio-sdk interface sync Screen.jsx \
--title "Sentiment Review" \
--workspace 3Create a project that uses the synced interface:
label-studio-sdk interface start . \
--project-title "Sentiment Review Project" \
--workspace 3An interface file is a JSX module whose final expression returns an object. The object must include a default function component.
function Screen({ task, regions, setRegions, params }) {
return <div>{task.data.text}</div>;
}
function getResults(regions) {
return regions;
}
function parseResults(results) {
return { regions: results };
}
({
default: Screen,
getResults,
parseResults,
inputSchema: {
type: "object",
properties: {
text: { type: "dataField", default: "text" },
},
},
outputSchema: {
type: "object",
properties: {
sentiment: { type: "string", enum: ["Positive", "Negative"] },
},
},
paramsSchema: {
type: "object",
properties: {
mode: { type: "string", default: "review" },
},
},
});Important exports:
default: required React function component.getResults(regions, relations): serializes interface state into Label Studio annotation results.parseResults(results): deserializes Label Studio annotation results into{ regions: [...] }.inputSchema: declares task data fields used by the interface.outputSchema: declares fields produced by the interface and used by Prompter-style auto-labeling.paramsSchema: declares per-project configuration passed into the component asparams.regionSchema: optional schema metadata for custom region shapes.specVersion: optional numeric compatibility marker.
The CLI compiles JSX with the classic React runtime. Do not use import statements in the interface file; use globals provided by the Label Studio interface runtime, such as React, hooks, getField, and EditorUI.
Scaffold a starter Screen.jsx, task.json, and scenarios.js.
label-studio-sdk interface init ./my-interface
label-studio-sdk interface init ./my-interface --forceWithout --force, the command refuses to overwrite existing scaffold files.
Run offline validation.
label-studio-sdk interface validate ./my-interface
label-studio-sdk interface validate Screen.jsx
label-studio-sdk interface validate Screen.jsx --scenario scenarios.js
label-studio-sdk interface validate Screen.jsx --jsonStatic validation checks:
- JSX compilation
- undefined JSX component references
- final module object shape
- schema export shape
- missing optional exports
getResultsandparseResultssmoke testsoutputSchemaandparseResultsconsistency
Scenario validation uses Playwright in a minimal React harness. Scenario files must default-export an array of scenario objects:
export default [
{
name: "selects a label",
task: { data: { text: "Example text" } },
async run({ page }) {
await page.getByRole("button", { name: "Positive" }).click();
},
expect: {
visibleText: ["Example text"],
results: [
{
from_name: "sentiment",
type: "choices",
value: { choices: ["Positive"] },
},
],
},
},
];The scenario context includes:
page: the Playwright page.getResults(): reads serialized annotation results from the harness.parseResults(results): calls the interface parser.setParsed(parsed): replaces harness state from parsed results.getState(): reads current harness regions, relations, and selection.getOutputSchema(): reads the interfaceoutputSchema.
Start the local interface playground and live-sync source changes.
label-studio-sdk interface preview ./my-interface
label-studio-sdk interface preview Screen.jsx --task task.json
label-studio-sdk interface preview Screen.jsx --task task.json --no-open
label-studio-sdk interface preview Screen.jsx --lse-url http://localhost:8080
label-studio-sdk interface preview Screen.jsx --offlinepreview downloads the version-compatible playground and sandbox artifacts from the configured Label Studio instance,
verifies them, and stores them in an origin- and protocol-scoped user cache. The first uncached run requires
LABEL_STUDIO_API_KEY; the CLI sends it with the same Token or Bearer authentication used by other SDK commands.
Before downloading public static assets, it probes GET /api/current-user/whoami and fails closed on 401/403 when
no verified cache exists. It never retries an authenticated asset request anonymously.
The CLI then starts two listeners bound only to 127.0.0.1: one hosts the playground and localhost SSE file updates;
the other hosts the isolated sandbox shell. Both use unguessable capability paths. The API token is used only by the
CLI during artifact bootstrap and is never placed in browser JavaScript, HTML, query parameters, or local storage.
Treat the printed capability URL as workstation-local sensitive data and do not publish it. This preview layer does
not expose a Save gateway or proxy Label Studio API requests.
Pass a directory to use the scaffolded bundle defaults: Screen.jsx for the interface and task.json or sample.json
for example task data. Source and task saves travel over localhost SSE only—there is no Django playground stream,
Redis, Streamer, WebSocket, long poll, or long-lived Label Studio request.
On later starts, the CLI makes a short authenticated manifest revalidation request. If Label Studio is unreachable or
returns an authentication error and a verified cache exists, preview starts from that cache and prints a stale-artifact
warning. --offline skips all network access and requires an existing verified cache. A protocol mismatch fails before
the browser opens. The server's selected artifact path does not change during the process; cache cleanup retains the
active snapshot and one prior snapshot.
View-Only users cannot mint the PAT needed for first-run bootstrap. Cookie SSO, OAuth proxies, and IAP browser bootstrap
are out of scope; allow access to /react-app/local-playground/ and /react-app/editor-standalone/, or populate the
cache from a reachable instance. Cached live preview still works without a token. validate remains fully local, while
sync, pull, and start remain authenticated server operations.
Create or update a saved interface. sync creates an unpublished local draft by default so you can review it in Label Studio before publishing.
label-studio-sdk interface sync Screen.jsx --title "Review UI"
label-studio-sdk interface sync Screen.jsx --id 12 --message "Tighten parser"
label-studio-sdk interface sync Screen.jsx --id 12 --publish
label-studio-sdk interface sync Screen.jsx --workspace-title "Data Science"
label-studio-sdk interface sync Screen.jsx --dry-runBy default, sync:
- validates and compiles the file
- creates a new interface when no id or sidecar entry exists
- updates the existing interface when
--idor a sidecar entry is available - appends a new draft version without replacing existing versions
- skips upload when the local source hash has not changed
- writes a sidecar file next to the JSX source
Useful options:
--id: update a specific interface id.--title: set the interface title.--workspace: use a workspace id for newly created interfaces.--workspace-title: resolve a workspace by exact title.--publish: create a published version instead of a local draft.--no-validate: skip the validation gate, while still requiring successful compilation.--force: upload even when hashes match.--dry-run: print the planned action without writing to the server.--message: store a history message for the synced version.
Pull a saved interface into a local bundle.
label-studio-sdk interface pull --id 12 ./review-ui
label-studio-sdk interface pull --id 12 --version 4 ./review-ui
label-studio-sdk interface pull --id 12 ./review-ui --forcepull writes Screen.jsx, task.json, optional params.json defaults from paramsSchema, and a sidecar file. Without --version, it pulls the latest version, including local drafts.
Sync the interface, create a project that uses it, and open the project data page.
label-studio-sdk interface start Screen.jsx \
--project-title "Review Project" \
--params params.jsonstart syncs a published version so the new project can safely reference it. --params must point to a JSON file. The JSON is saved as the project interface params and is passed into the component as params.
When you pass a directory, start uses Screen.jsx for the interface and params.json for project params when present.
Useful options:
--id: sync against an existing interface id.--title: title to use if a new interface is created.--project-title: project title. Defaults to<interface title> Project.--description: project description.--workspace: workspace id for the interface and project.--workspace-title: workspace title for interface creation.--force: sync even when the source hash is unchanged.--no-open: create the project without opening a browser.
Open a saved interface using the local sidecar file.
label-studio-sdk interface open Screen.jsx
label-studio-sdk interface open Screen.jsx --no-openRun sync first so the sidecar contains the interface id for the selected Label Studio URL.
Check prerequisites and connectivity.
label-studio-sdk interface doctor
label-studio-sdk interface doctor --lse-url http://localhost:8080The command checks Node.js, npm, the API token, validator dependencies, LSE reachability, and API authentication separately. It also reports the preview cache path, origin key, protocol, artifact fingerprint, and last successful fetch.
After a successful sync or pull, the CLI writes a sidecar next to the source file:
Screen.jsx.ls-interface.json
The sidecar is keyed by Label Studio base URL and stores:
- interface id
- interface title
- workspace id
- source version
- last pushed source hash
- last pushed timestamp
This lets future preview, sync, start, and open commands find the saved interface without requiring --id.
Preview includes the matching-origin ID in local file-update events. A sidecar source version can point at a draft
version until you publish it.
For API calls, the CLI resolves configuration in this order:
- explicit command option
- parent CLI context
- environment variables
- default base URL
https://app.humansignal.com
Token environment variable:
LABEL_STUDIO_API_KEYBase URL environment variables:
LABEL_STUDIO_URL
LS_URLTokens that look like JWTs use Bearer authentication. Other tokens use Label Studio Token authentication.
label-studio-sdk interface doctor reports the local preview protocol and whether a verified artifact exists for the
configured canonical Label Studio origin. Reachability, API authentication, validator setup, and preview cache health
are separate checks so an unreachable server does not make a valid offline cache appear corrupt.
Preview's cache is stored in the platform user cache directory under label-studio-sdk/interface-preview. Cache slots
are keyed by canonical origin (scheme, host, and port) and local protocol version; artifacts from different Label Studio
instances or protocol majors are never mixed.
node is required for interface validation
Install Node.js and make sure node is available on PATH.
npm is required to install validator dependencies
Install npm and rerun label-studio-sdk interface doctor.
source did not compile
Fix JSX syntax or unsupported module syntax. Interface files are compiled as self-contained JSX snippets, not bundled application modules.
Module did not return an object
Make the final expression in the file an object literal wrapped in parentheses, for example ({ default: Screen }).
Missing getResults or Missing parseResults
These are warnings during static validation but required for scenario validation. Add both functions when you need reliable save/load behavior.
no sidecar entry
Run label-studio-sdk interface sync Screen.jsx for the target Label Studio URL, or pass --id to commands that support it.
offline preview requires a verified cache
Run preview once without --offline against a reachable Label Studio Enterprise instance using a valid API key.
preview protocol mismatch
The installed SDK and the configured Label Studio instance do not share a local-preview protocol. Update the older side before retrying; the CLI will not serve an incompatible cached artifact.
failed to download preview assets and no verified cache exists
Confirm the instance includes /react-app/local-playground/manifest.json, that the API key is valid, and that any
fronting proxy allows the local-playground and editor-standalone static paths.
multiple workspaces titled ...
Pass --workspace with the numeric workspace id instead of --workspace-title.