Audience Labels
Audience labels let a single assistant serve several groups from the same project while keeping each group’s knowledge private to that group.
In plain English: one assistant can have a single knowledge base, but different people should only see the shelves that are relevant to them. If a learner is in course A, they should not retrieve course B’s materials. If an instructor is in a different role, they may see a different subset of the same project.
The classic case: you ingest documents through the API on behalf of a customer whose source system (an LMS, an intranet, a document store) had per-user permissions. Once the content is in Ragtime, those permissions are gone. Audience labels restore a coarse version of them.
A good mental model is a library as a locked cabinet. The cabinet has labels on
it such as lti:context:course-4821 or lti:role:instructor. A visitor carries
a verified set of labels from a trusted launch. Ragtime only opens the cabinet
when the labels overlap.
The model
Section titled “The model”- A library is the permission boundary. There are no per-document or per-chunk ACLs — if a visitor can retrieve from a library, they can retrieve anything in it.
- Each link between a project and a library carries a list of audience labels. An empty list means “unrestricted”.
- A visitor carries a set of verified labels, derived server-side.
- Retrieval returns a library only if it is unrestricted, or if the visitor’s labels overlap the library’s labels.
Because the filter is applied inside the retrieval query rather than after it, restricted content never enters the model’s context, never appears in citations, and never leaks through diagnostics.
Where labels come from
Section titled “Where labels come from”Visitor labels are only ever derived from a source Ragtime can verify. They are never read from a request body, query string, or header — a caller-supplied label would be a trivial authorization bypass.
Ragtime derives labels from verified organization membership, organization roles, project membership, a specific signed-in user, and LTI 1.3 launches. LTI launch tokens are verified against the learning platform’s public keys before their context and role values become available as audience rules.
Set Audience source on the channel (Dashboard → Deployment → Channels):
| Setting | Effect |
|---|---|
None |
Visitors carry no labels. Only unrestricted libraries are searchable. |
LTI launch |
Labels are derived from the verified launch. |
An LTI launch produces two kinds of label:
lti:context:<contextId> # the course / context the learner launched fromlti:role:<role> # instructor, learner, contentdeveloper, …Roles are normalised: the full IMS role URI
http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor and the short form
Instructor both collapse to lti:role:instructor.
Restricting a library
Section titled “Restricting a library”Go to Dashboard → Settings → Access, find the linked library, and use the tag picker in the Audience column. Leave it empty to keep the library unrestricted.
For your own groups, enter ordinary labels such as staff or customers. For
identity-based access, select a friendly verified rule:
- all members of an organization
- an organization role
- members of a project
- a specific organization member
- a context or role observed in a successful verified learning-platform launch
Ragtime stores the canonical org:…, org-role:…, project:…, user:…, or
lti:… value behind the friendly choice. Do not type those internal identifiers
yourself. Manually invented reserved values are rejected.
Custom labels are lowercased and de-duplicated on save. A link may hold up to 50 labels.
Managing labels over the API
Section titled “Managing labels over the API”If the groups come from a system that already knows them, you probably do not
want to maintain labels by hand. Two scopes cover this, and they are deliberately
separate from ingest: a compromised ingestion connector must not be able to
clear a library’s labels and expose it to everyone.
| Scope | Grants |
|---|---|
audience:read |
Read a library’s labels |
audience:write |
Replace a library’s labels |
Read the current labels:
curl https://your-ragtime-instance.com/api/v1/projects/{projectId}/libraries/{libraryId}/audience \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE"{ "libraryId": "…", "audience": ["lti:context:course-4821"], "restricted": true, "availableDerivedAudiences": [ { "value": "lti:context:course-4821", "label": "Academy: context course-4821", "description": "Anyone arriving through this verified course or learning context.", "group": "Learning platform" } ]}When the calling key also has audience:write,
availableDerivedAudiences is the authoritative list of reserved values it may
submit. Read-only audience keys do not receive this derived-rule catalog.
Use the friendly metadata in configuration interfaces instead of asking an
operator to type internal IDs. Ordinary custom labels do not need to appear in
this list. The project-scoped API catalog excludes specific-user rules and sibling
projects to avoid exposing organization-wide member or project metadata; configure
specific-user rules in the dashboard.
Replace them. PUT overwrites the whole list rather than merging, so send the
complete set every time:
curl -X PUT https://your-ragtime-instance.com/api/v1/projects/{projectId}/libraries/{libraryId}/audience \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{"audience": ["lti:context:course-4821", "lti:context:course-4822"]}'Sending {"audience": []} removes the restriction and makes the library visible
to every visitor of the project. This is a widening operation and is recorded in
the audit log along with the previous value.
Labels over the limits are rejected, not silently dropped — a caller that believes it restricted a library but did not is the exact failure this feature exists to prevent.
Reserved values are also rejected unless they are currently present in
availableDerivedAudiences or already stored on that library. An unavailable
legacy value may remain while other labels are edited, and can always be removed,
but it cannot be newly added. A PUT with such a value returns 400 and lists
the offending values in details.invalidLabels:
{ "code": "invalid_request", "error": "Some derived audience labels are not available to this project.", "retryable": false, "hint": "Use only values listed in `availableDerivedAudiences` from GET on this endpoint (requires the `audience:write` scope).", "details": { "invalidLabels": ["lti:context:course-9999"] }}A library that is not linked to the project returns 404. Authentication, scope,
project and rate-limit refusals work the same on every endpoint; see
Errors and
Rate limits.
GET /api/v1/projects/{projectId}/libraries requires the ingest scope. When
the key also holds audience:read, each entry it returns gains audience and
restricted, which is the easiest way for a sync
job to reconcile state in one call. Keys without that scope see the list without
them, since label names can themselves reveal cohort names.
Behaviour you should expect
Section titled “Behaviour you should expect”- Existing projects are unaffected. Every pre-existing link has an empty audience, which means unrestricted.
- No labels means unrestricted-only, not deny-all. A visitor with no verified labels still reaches every library you have not restricted.
- Organization members are also filtered. A labelled library is available only when the member presents a matching derived or granted label.
- A restricted library is invisible, not empty. It does not appear in retrieval diagnostics either, so its name cannot be inferred.
- Assistant topics follow the same rule. Topics from restricted libraries are excluded from the compiled system prompt, because that prompt is shared across every visitor to the assistant.
Common misconfiguration
Section titled “Common misconfiguration”If you label a library but leave the channel’s Audience source on None, that
library becomes unreachable by everyone — no visitor can produce a matching
label. The dashboard warns about this, but it is the most likely mistake when
first setting things up.
What this is not
Section titled “What this is not”Audience labels are a coarse partition, deliberately. They do not reproduce per-user document permissions from a source system, and they do not attempt to track individual users. If two people belong to the same course, they see the same knowledge. Model your libraries around the groups that genuinely need different answers, not around individual users.
If you need a fully separate persona, prompt, or model as well as separate knowledge, use a separate project instead.
Related
Section titled “Related”- LTI 1.3 (LMS) — how launches are verified.
- API Keys — restricting a key to a single library.
- Ingest API — pushing content into a specific library.