Skip to main content
Tags are flat, reusable labels you attach to documents to group them into collections: a project, a team, a topic, a customer. Unlike Facets, there’s no schema and no hierarchy to design first, and unlike Workspaces, a tag isn’t tied to one container: the same tag can group files that live in different workspaces, so you can form cross-workspace collections. You create a tag, assign it to files, and immediately scope search, ask, and file listings to that collection.
“Search only the Project Alpha documents.” “Answer this question using just our compliance files.”
That’s what tags give you: a lightweight way to carve your corpus into named groups your app can filter on.
This tutorial covers the tag lifecycle: POST /api/v3/tags, GET /api/v3/tags, POST /api/v3/files/{id}/tags, and DELETE /api/v3/files/{id}/tags/{tag_id}. Assigning tags at upload time is covered in Uploading & managing files.

Workspaces, tags, or facets?

All three organise documents, but at different layers. They compose: a file lives in one workspace, can carry several tags, and can be classified with facets.
WorkspacesTagsFacets
What it isA container; every file lives in exactly oneFlat, reusable labelsTyped, hierarchical metadata with a schema
A file belongs toexactly one workspacemany tags, across workspacesmany content types, across workspaces
Best forIsolating teams, customers, tenantsCross-cutting collections (project, topic)Precise structured queries
Setup costCreate a workspaceCreate a tagDesign a content-type tree
Scope a query withworkspace_idtag_idcontent_type / attribute
Access controlYes: API keys can be scoped to a workspace with a per-key roleNo: not a permission boundaryNo: not a permission boundary
Only workspaces are a permission boundary: API keys can be scoped to specific workspaces, so reach for workspaces (not tags) when different data needs different permission levels. Tags are the right tool when a collection spans workspaces, for example a Project Alpha tag on files that live in both the Engineering and Design workspaces. You can combine tag_id with workspace_id to scope to a tagged collection within a single workspace. The rest of this tutorial covers tags.

Step 1: Create a tag

A tag has a name and a description. Create it once at the company level, then reuse it across as many files as you like.
create_tag.py
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['LIGHTON_API_KEY']}"}

response = requests.post(
    "https://api.lighton.ai/api/v3/tags",
    headers=headers,
    json={
        "name": "Project Alpha",
        "description": "Documents related to the development and release of Project Alpha",
    },
)
print(response.json())
A 201 returns the new tag with its id. Note it down: you’ll use the ID everywhere else.
{
  "id": 7,
  "name": "Project Alpha",
  "description": "Documents related to the development and release of Project Alpha",
  "auto_assign": true,
  "document_count": 0
}
auto_assign controls who can attach the tag. It defaults to true, meaning LightOn’s AI can assign the tag to matching documents automatically (your description is the signal it matches on, so write it well). Set it to false for a manual-only tag that the system never touches.

Step 2: Find a tag’s ID

Tags are company-scoped, so a teammate may already have created the one you need. List them, optionally filtering by name, to grab the ID.
list_tags.py
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['LIGHTON_API_KEY']}"}

# Find a tag by name (case-insensitive partial match) to get its ID
response = requests.get(
    "https://api.lighton.ai/api/v3/tags",
    headers=headers,
    params={"name": "alpha"},
)
print(response.json())
The response is paginated. Each tag carries a document_count so you can see how populated a collection is.
{
  "count": 1,
  "results": [
    {
      "id": 7,
      "name": "Project Alpha",
      "auto_assign": true,
      "document_count": 12
    }
  ]
}

Step 3: Assign tags to files

You have three ways to attach tags, depending on timing:
  • At upload time: pass tags in the upload payload. See Uploading & managing files.
  • Add to an existing file: POST /api/v3/files/{id}/tags. Adds tags without disturbing the ones already there.
  • Replace every tag: PATCH /api/v3/files/{id} with a new tags array. This overwrites all existing tags, manual and auto-assigned alike.
For incremental tagging, adding is what you want.
add_tags.py
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['LIGHTON_API_KEY']}"}

file_id = 1234  # replace with your file ID

# Adds these tags without touching tags already on the file
response = requests.post(
    f"https://api.lighton.ai/api/v3/files/{file_id}/tags",
    headers=headers,
    json={"tags": [3, 7]},  # tag IDs from GET /api/v3/tags
)
print(response.json())
This returns the full file object with its updated tags array. Duplicate tags are ignored (no error), and newly added tags are marked auto_assigned: false.

Step 4: List files in a collection

Once files are tagged, filter the file listing by tag_id to retrieve a collection.
filter_by_tag.py
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['LIGHTON_API_KEY']}"}

# All files carrying tag 7, most recent first
response = requests.get(
    "https://api.lighton.ai/api/v3/files",
    headers=headers,
    params={"tag_id": "7", "ordering": "-created_at"},
)
print(response.json())
You get back a paginated list of files carrying that tag. Combine tag_id with other filters (extension, workspace_id, total_pages_min) to narrow further.

Step 5: Scope search and ask to a collection

This is where tags earn their keep. Both POST /api/v3/search and POST /api/v3/ask accept a tag_id array that restricts the query to documents in those collections. Pass several IDs to widen the scope to a union of collections. Search across a collection:
search_by_tag.py
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['LIGHTON_API_KEY']}"}

# Search only within the collection tagged 7 (combine several IDs to widen it)
response = requests.post(
    "https://api.lighton.ai/api/v3/search",
    headers=headers,
    json={
        "query": "rollout timeline and milestones",
        "tag_id": [7],
    },
)
print(response.json())
Ask a grounded question over a collection:
ask_by_tag.py
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['LIGHTON_API_KEY']}"}

# Grounded answer over only the documents tagged 7
response = requests.post(
    "https://api.lighton.ai/api/v3/ask",
    headers=headers,
    json={
        "query": "What is the launch date for Project Alpha?",
        "tag_id": [7],
    },
)
print(response.json())
tag_id combines with workspace_id (both narrow the corpus together), but is mutually exclusive with file_id.

Step 6: Remove a tag

To take a single file out of a collection, delete that one association. The file and the tag both survive; only the link is removed.
remove_tag.py
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['LIGHTON_API_KEY']}"}

file_id = 1234  # replace with your file ID
tag_id = 7      # replace with the tag to remove

# Idempotent: returns 204 even if the tag was not on the file
response = requests.delete(
    f"https://api.lighton.ai/api/v3/files/{file_id}/tags/{tag_id}",
    headers=headers,
)
print(response.status_code)
A 204 confirms success. The call is idempotent: you get 204 even if the tag wasn’t on the file. To retire a tag everywhere, DELETE /api/v3/tags/{id}. That removes the tag from every document it was attached to, so use it deliberately.

Next steps

Uploading & managing files

Assign tags at upload time and manage file metadata

Searching documents

Rank passages, then scope the search by tag

Asking questions

Get grounded answers over a tagged collection

Organizing documents with metadata

Step up to typed, structured metadata with facets