Workspaces
Workspaces
Make workspaces, add files, search and ask questions, and manage it all in the dashboard or from code.
A workspace holds files you want answers from: contracts, ledgers, reports, spreadsheets, emails, recordings. Upload them once, then search them, and every answer comes with the passages it rests on.
Workspaces live in the dashboard under File search, in the Playground group of the sidebar, and API keys reach them through the /v1/workspaces routes with the same access. Everything below is shown both ways: what to do in the dashboard, and the request for the same thing from code. Reference pages such as Automatic search list every field, and Files and ingestion covers a workspace's files in full. The Perception routes work on single files instead. Creating, reading, updating and deleting workspaces costs nothing and works at any balance.
An upload, upload grant or upload session that names no workspace_id is uploaded to the server as a source for the Perception tools, and is not part of any workspace; one that names a workspace goes into it. See Uploads. There is no default workspace: name the one you mean.
In the CLI, each command that works on one workspace takes --workspace ID. Without it, the CLI uses $NACE_WORKSPACE_ID, then the workspace nace workspace-use ID saved (nace workspace-use --clear forgets it); with none, the command exits with a usage error that says how to pick one. nace workspace-delete is the exception: it deletes only the workspace --workspace names. nace workspace prints the workspace commands use now. Uploads take only --workspace: without it, the file is uploaded to the server, whatever $NACE_WORKSPACE_ID or a saved workspace say.
Make one
Open File search and choose New workspace. The page starts from a name and, if you like, files: drop them, browse, or add your recent uploads, or add files later. Files start uploading as soon as you add them, each row showing how far it is, into a draft only you can see. Create turns the draft into the workspace and starts processing without waiting for uploads still going; they carry on on the workspace's page. Leave the page instead and the draft stays: the next time you open New workspace it offers the files back, to Continue or Discard. You have one draft at a time, and it doesn't expire. A workspace is just a name, which no other workspace in the account may share, ignoring case: making one uploads nothing and costs nothing. Choose a Domain too: the categories its files are sorted into. General is the default, and the list also holds the domains your account published (on the Domains page beside the workspace list under File search) and the ones Nace provides. It can't be changed later. Domain and storage are set when the first file uploads: changing either afterwards uploads the files again, once you confirm. A draft continued from an earlier visit keeps both; discard it to change them. Its files are kept until someone deletes them, the workspace or the account, and the Files tab says so before you upload. Zero retention remains a separate mode. Standard files and derived outputs stay until deletion.
From code:
curl https://console.nace.ai/v1/workspaces \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "fy25-audit" }'The answer is 201 with the workspace. Creating a workspace costs nothing. Only name is required; every other field has a default. Unrecognized fields are refused with 422 invalid_request. A name must be unique among the account's workspaces, ignoring case: a name already in use is 409 conflict_error, code workspace_name_taken, and a deleted workspace's name is free again. A create isn't idempotent: if one went through and you send it again, the second answers 409, so the SDKs don't retry it unless you pass a retry policy.
| Field | What it does |
|---|---|
name | Required. 1 to 120 characters once surrounding whitespace is trimmed, and unique in the account, ignoring case. Rename it through the dashboard or a name-only PATCH /v1/workspaces/{id} request. |
domain_slug | The published domain whose categories the workspace sorts its files into: generic, the general-purpose default; a domain your account published; or one Nace provides. The workspace pins the newest published version at that time. Default generic. It can't be changed later. A slug that names no published domain you can use is 422 invalid_request. |
access | Access labels: labels, a list of up to 16 { "name", "description" } objects, and default_label. Ingestion gives each file one label, using the description as the classifier's prompt, and search then reports what it withheld. A name is lowercase letters and digits joined by _. With labels, default_label is required and must be one of them; it's applied when the classifier can't decide. The default, labels: [], turns labels off. They can't be changed later. |
List, read and stats
In the dashboard, the File search page lists every workspace you can use, and opening one shows its question box, files and activity. From code:
GET /v1/workspaces lists the workspaces you can use, oldest first. name_contains keeps those whose name contains it, ignoring case. limit is 1 to 200, default 50; anything else, or a cursor that isn't one, is 422 invalid_request. The page is { "items", "next_cursor", "total_count" }: pass next_cursor as cursor for the next page until it's null. total_count counts every match, across pages. A workspace the document service no longer has stays in its place with status unavailable, so it never fails the page; delete it to take it off the list.
GET /v1/workspaces/{workspace_id} reads one. An id that isn't a workspace you can use, or one that's been deleted, is 404 workspace_not_found. Every route under /v1/workspaces/{workspace_id} checks the workspace first, so such an id gets this 404 before the body, the parameters or the Idempotency-Key are checked.
curl "https://console.nace.ai/v1/workspaces?name_contains=audit" \
-H "Authorization: Bearer $NACE_API_KEY"
curl https://console.nace.ai/v1/workspaces/$WORKSPACE_ID/stats \
-H "Authorization: Bearer $NACE_API_KEY"list returns one page (limit, cursor); iter_all (iterAll) follows every page. In the CLI, nace workspaces takes --limit, --cursor and --all, and ends a page with next: <cursor> when more follow; nace workspace prints one workspace. nace workspace-stats prints one fact per line, and leaves out the ingestion: line when no status is counted.
GET .../stats summarizes what's in the workspace:
| Field | What it holds |
|---|---|
files_total, bytes_total | How many files it holds, and their size. |
files_by_ingestion_status | Files per ingestion_status. stale means a file changed since it was ingested, and expired that a historical expiry dropped its outputs before automatic content retention was removed: ingest them again to make them searchable. |
pages_ingested, index_documents | Pages ingested, and entries in the search index. |
last_ingestion_at | When the last ingestion finished, or null. |
next_expiry_at, files_expiring_within_7d | Legacy compatibility fields: null and 0; ordinary content no longer expires automatically. |
Add files
Drop files or whole folders on the Files tab, or use Upload. Folders keep their structure. Large files go up in parts, straight to the document service, so there is no size limit beyond its own per-type caps. Uploading a file to a path that already has one makes a new version of it.
From code, upload each file to a workspace you name:
curl https://console.nace.ai/v1/documents/upload-grants \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d "{ \"workspace_id\": \"$WORKSPACE_ID\", \"path\": \"reports/q4.pdf\" }"
# {"upload_url":"https://.../files","token":"...", ...}
curl "$UPLOAD_URL" \
-H "X-Upload-Token: $TOKEN" \
-F file=@q4.pdf \
-F 'metadata={"path":"reports/q4.pdf"};type=application/json'While a file uploads, its row in the Files tab says how far it is: Waiting, Uploading, Finishing… while the parts of a large file are joined, or Paused while your browser is offline, which carries on when the connection is back. An upload that fails says why on hover and can be tried again while the tab is open; the X stops an upload. Empty files, and a file you already added, are skipped.
Files are processed on their own once they have uploaded, a few at a time as they land: each shows Queued until its turn, Processing while it is read, then Processed, and only then can a search or a question find it. One count above the list says how many of the files you added are ready, and while the rest process the search box says how many are searchable. Failed means the file could not be read: hover it for the reason, and process it again from its row. Changed means a new version is waiting to be processed.
From code, nothing processes a file on its own: ingest it once the upload is done.
curl "https://console.nace.ai/v1/workspaces/$WORKSPACE_ID/ingestions?wait_seconds=60" \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "selection": { "type": "path_prefix", "path_prefix": "" } }'Open a file to preview it, see its details and versions, or download it. Select files to delete them; deleting removes them, and everything derived from them, for everyone in the workspace.
From code, list the files, then delete one by its file_id:
curl "https://console.nace.ai/v1/workspaces/$WORKSPACE_ID/files?path_prefix=reports/" \
-H "Authorization: Bearer $NACE_API_KEY"curl -X DELETE "https://console.nace.ai/v1/workspaces/$WORKSPACE_ID/files/$FILE_ID" \
-H "Authorization: Bearer $NACE_API_KEY"The other tabs
- Activity shows file uploads, file deletions, Search runs, with who did each. Filter it by activity, user or date; a row opens the files or the answer. It has no API route.
- People is where anyone in the account invites people from outside it. Inviting is dashboard-only; the API has no route for it.
From code, ask a question as the box does:
curl "https://console.nace.ai/v1/workspaces/$WORKSPACE_ID/automatic-search?wait_seconds=60" \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "query": "What were total liabilities at year end?" }'To have your own code agent search the workspace instead, give it the navigate tools and the agent skill.
Searches, questions and processing runs also appear in Usage › Logs, each linking back to its result.
Rename one
Open the workspace's actions menu and choose Rename. Its creator or the owner account's owners and admins can save a new name. The workspace keeps its files, searches and links. Deletion confirmation uses its current name.
API callers send PATCH /v1/workspaces/{id} with {"name":"New name"}. Send the name separately from settings: a mixed request is rejected before changes. Names are unique within the account ignoring case; a conflict returns 409 workspace_name_taken.
Deletion checks the confirmed name against the current name when the server validates the request. If another user renames the workspace first, reload and confirm its new name. A rename overlapping a deletion that has already been validated does not cancel that deletion.
Delete one
The account's owners and admins, and whoever made the workspace, can delete it from the Workspaces list after typing its name. Every file in it goes, for good. People you invited still see it in their list, marked as deleted by its owner, until they remove it.
From code, name the workspace again to confirm:
DELETE /v1/workspaces/{workspace_id} deletes the workspace and everything in it: every file and version, ingestion's outputs and the search index. It can't be undone. Only the owner account's owners and admins, or whoever created the workspace (its owner_user_id), can delete it; anyone else, including people invited from outside the account, gets 403 access_denied. An API key acts as the member who created it. The body must carry confirm_name, exactly the workspace's current name as checked when the request is validated (see Rename one); anything else is 422 invalid_request. The 403 is permission_error, code access_denied.
curl -X DELETE https://console.nace.ai/v1/workspaces/$WORKSPACE_ID \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"confirm_name": "fy25-audit"}'The answer is 202 with a workspace_delete job: read it like any other, with GET /v1/documents/jobs/{job_id} or jobs.wait, even after the workspace is gone; see Jobs and results. The body takes only confirm_name, and the delete takes no wait_seconds or Idempotency-Key. A workspace the document service had already let go answers 204 with no job, and the SDKs return None (null in TypeScript). The CLI waits for the job unless you pass --async. The MCP server's delete_workspace returns the job without waiting.
The job's result has workspace_id, the counts of what was deleted (files_deleted, index_documents_deleted), prefix_deleted, and verified, which is true once the deletion is confirmed complete. The workspace stops listing, and answers 404 workspace_not_found, as soon as the delete is accepted, while the job finishes the teardown.
Domains
A domain is the two-level taxonomy a workspace classifies its files against: a list of classes, each a group and a subgroup with a description written for the classifier. Your account can author domains and use any platform domains returned in its catalog. Use the domain_id from GET /v1/domains for reads and duplication (a slug there is 404), and the slug only to filter that list. A workspace can report the default generic even when that slug has no entry in the current catalog; an empty filtered list supplies no domain to read or duplicate. In the dashboard, they are under File search, Domains.
A domain version is a draft or published. A draft is edited in place. Publishing freezes it, one way, and only a published domain can be given to a new workspace. A published domain, and the platform's, never change: to change one, duplicate it and edit the copy. A workspace is pinned to the version it was made with, so editing a copy never reaches it.
| Call | What it does |
|---|---|
GET /v1/domains | Lists the domains currently returned by the document service for your account, with owner, status and slug filters. Results are paginated: pass limit and follow next_cursor as cursor until it is null; a matching domain may be on a later page. slug matches a returned slug exactly; no match gives an empty list. |
POST /v1/domains | Creates a draft from a slug (lowercase letters, digits and single underscores, up to 64), a display_name and, optionally, a taxonomy. |
GET /v1/domains/{domain_id} | Reads one domain version with its taxonomy. |
PATCH /v1/domains/{domain_id} | Renames a draft or replaces its whole taxonomy. A published domain is 409 domain_not_editable, and a platform one 403 access_denied. |
POST /v1/domains/{domain_id}/publish | Publishes a draft. A taxonomy that fails validation is 422 domain_validation_failed, with the rules to fix in detail.validation. |
POST /v1/domains/{domain_id}/duplicate | Makes a draft copy of any domain you see, the platform's included. |
Managing domains costs nothing. Each call is in the API reference. A taxonomy is { "classes": [{ "group", "subgroup", "description", "aliases" }] }, and a document that fits no class is labelled as such, not forced into one.
# Author a domain, publish it, and make a workspace against it
curl https://console.nace.ai/v1/domains \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"slug": "leases",
"display_name": "Leases",
"taxonomy": { "classes": [{
"group": "leases", "subgroup": "amendments",
"description": "Amendments to a signed lease.", "aliases": []
}] }
}'
curl -X POST https://console.nace.ai/v1/domains/$DOMAIN_ID/publish \
-H "Authorization: Bearer $NACE_API_KEY"
curl https://console.nace.ai/v1/workspaces \
-H "Authorization: Bearer $NACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "fy25-leases", "domain_slug": "leases" }'Access
A workspace belongs to the account that created it. Any member of that account can use it, and so can people invited into it from outside the account, until they leave or are removed. Invitations are sent from the dashboard; see Sharing. An invited person can work with the files, search and ask questions, but can't delete the workspace. An API key reaches the workspaces the member who created it can use.
The workspace object
| Field | What it holds |
|---|---|
workspace_id | The id every route on this page takes, and the workspace_id of a workspace_file source. |
name | Its current display name (see Rename one). |
owner_user_id | The account member who created it: the user the creating API key acts as. They can delete it. |
domain_slug, domain_id, domain_version | The domain it is pinned to: its slug, the id of the pinned version, and that version's number. |
access | Its settings, as in the table above. |
config_version | The version of the settings its outputs are derived under. Starts at 1. |
status | active. A workspace that's being deleted answers 404 workspace_not_found instead. In a list, unavailable marks a workspace the document service no longer has: it carries only workspace_id, name, owner_user_id and created_at, reading it is 404 workspace_not_found, and deleting it takes it off the list. |
storage | Where its files are kept: {"type": "managed"}, or for your own storage {"type": "external", "connection_id", "provider", "location", "health"}. In a workspace you were invited into, connection_id and location are null. |
created_at, updated_at | When it was created and last changed. |
Use your own storage
Customer storage is available only where it has been enabled for your deployment. Elsewhere the creation form has no storage choice and new workspaces use managed storage. Workspaces already bound to your storage keep working, but connections can't be added or managed until it is enabled again.
A saved connection and its base prefix can be reused for multiple workspaces. Each workspace gets a separate ndi/workspaces/<workspace-id>/workspace/ folder under that prefix. The creation form shows the path pattern; after creation, Settings shows the exact path. Workspace IDs and paths are assigned by NDI. Reusing the connection does not share files between workspaces.
When creating a workspace in the console, the Storage field defaults to Nace storage for Standard accounts. Pick a verified connection from that field, or choose Connect S3 or Azure… to add one. Zero retention disables Nace storage and requires a customer connection before the workspace can be created. Switching back to Nace storage clears the selected connection. A workspace keeps the storage chosen at creation; existing workspaces are not moved. If customer storage is disabled for the deployment, Zero retention blocks workspace creation.
Connections belong to your account. Every account member can use a ready
connection for a new workspace. Its creator and account owners or administrators
can update access in Settings → Storage (/dashboard/settings#storage), also
linked from Manage beside the Storage field. Choose Connect storage there
to add a connection without creating a workspace. Inviting someone into a workspace
shares its files and tools, without granting access to your storage setup.
If your document service deployment does not yet support guided setup, the console shows manual instructions instead. For S3 it lists the trusted roles and this connection's exact External ID for your role's trust policy. For Azure, assign Storage Blob Data Contributor yourself as described below. In both cases you can still save credentials and verify access. Generated CloudFormation templates, IAM policies and Azure commands return once the service is updated.
Amazon S3
- In Bucket, enter an existing bucket and its AWS Region. Name and folder lets you add a display name and an optional base folder. Select Continue. Connection names are fixed once created. An unverified connection can go back to Bucket; changing its destination replaces the connection and requires setting up access again.
- In Access, enter the AWS account ID that owns the bucket. The setup saves as you type. Role name and KMS key provides optional settings. Choose Open AWS CloudFormation, review the requested IAM permissions and create the stack. If a prefilled launch isn't available, choose Download template and open CloudFormation, then upload that template in AWS. Return to the console after creating the stack.
- Alternatively, choose Set up the role manually instead. Use the generated trust and permissions policies, preserving unrelated statements when updating an existing role. Trust all listed Nace roles with this connection's exact External ID, then enter the role's Role ARN.
- Select Verify access. The Verify step checks both API and worker access and retries while a new AWS role propagates. Once access passes, the dialog closes and selects the connection in the workspace form. When opened from Settings, the connection becomes available for new workspaces.
The optional KMS key ARN adds permissions to the generated IAM policy. Your bucket still controls encryption; the KMS key policy must also allow your role. Verification checks the bucket, not an unused key in isolation.
Azure Blob Storage
In Bucket, enter an existing Storage account and Container. Name and folder adds an optional display name and base folder; the name can't be changed once the connection exists. Select Continue, then in Microsoft Entra, register or select an application and copy its Tenant ID and Application Client ID. Create a client secret and use its value, not its secret ID.
Select Continue to reach Access, then enter Tenant ID, Application (client) ID and Client secret. Under Assign the role with Azure CLI, enter Subscription ID and Resource group, then select Show commands. Run them as an administrator allowed to assign roles, or assign Storage Blob Data Contributor to that application's service principal on the container's Access control (IAM) page. This RBAC grant covers the container; the prefix does not narrow Azure permissions. Select Verify access; the console retries while a new role assignment propagates. Once access passes, the dialog closes and selects the connection in the workspace form. From Settings, the verified connection becomes available for new workspaces. Secrets are write-only.
With an API key
The same steps work with an API key, under the same rules: any member's key
can list connections and create a workspace with a ready one, and only a key
of the connection's creator or of an account owner or administrator can change
it. Where customer storage is off for the deployment, every one of these calls
answers 404 storage_connection_unavailable instead (a malformed storage on workspace create is 422 invalid_request first).
POST /v1/storage-connectionswith anameand adestination({"type": "s3", "bucket", "region", "prefix"}or{"type": "azure_blob", "account", "container", "prefix"}) creates a draft.PUT /v1/storage-connections/{id}/setupsaves your AWS account and role name, or your Azure subscription and resource group, and answers with the trust and permissions policies, the CloudFormation template and launch link, or theazcommands. Create the role or role assignment in your cloud.PUT /v1/storage-connections/{id}/credentialssaves the role ARN, or the tenant id, client id and client secret.POST /v1/storage-connections/{id}/validatestarts the check. Read it withGET /v1/storage-connections/{id}/validation-jobuntil it has succeeded; a new role can take a few minutes to apply, so retry a fresh one that fails.- Create the workspace with
"storage": {"type": "external", "connection_id": "<id>"}inPOST /v1/workspaces.
The Python SDK,
TypeScript SDK and
nace CLI wrap these calls.
Retention and access
Ordinary files, job payloads and derived outputs stay until explicit deletion. Zero retention requires customer-bound storage and clears Nace job content and transient copies while leaving customer objects untouched. Explicit file, workspace and account deletion still apply. Content-free billing and operational records follow their separate retention rules.
On the New workspace page, an account with a ready connection picks Storage before its files upload: added files wait until you choose, or until you press Create, which keeps Nace storage. Files then upload into the draft's own folder in your storage as you add them, before you press Create. Discarding a draft, or changing its storage after files uploaded, removes them from Nace but leaves them in your bucket, and the page says so. If a connection stops accepting files during an upload, the rows say so and link to Settings → Storage.
Revoking a connection pauses access for workspaces using it and leaves their files in your storage. There is no fallback to managed storage when access fails. Restore permissions and verify the connection again. An administrative revocation requires Nace support's approval before access can be restored.