Skip to main content

Scope, Models & Access

Scope an ask to a hard-filtered document slice, pick the model or model class under your AI policy, shape the output, and apply per-user compartment visibility.

By default an ask runs over the entire workspace corpus. The optional request fields on [POST /v1/ask](post-ask) let you narrow what the turn can read (scope), choose what model runs it (model, model_class), shape what comes back (output_format), and control whose visibility applies (on_behalf_of). Each of these is a contract with precise semantics, and this page is their reference.

Scope is a hard filter, not a hint

The scope object restricts the turn to a slice of the workspace, and the restriction is structural: the scope is compiled into a parameterized SQL predicate that is applied to every retrieval plane and every virtual table the agent can query. The registry match, the lexical value match, the document-text search, and every read-only SQL query the model writes all run against the scoped slice, so no query the model composes can widen it. This is what makes scope useful for isolation: a question scoped to one pipeline cannot leak evidence from another, no matter how the model phrases its retrieval.

Present fields are ANDed, so a scope narrows monotonically: {"pipeline_id": "...", "document_type": "Invoice"} matches only invoices in that pipeline. document_ids accepts at most 1,000 ids and tags at most 100 entries; requests above the cap are rejected with 400 naming the field, before any work starts. tags matches documents carrying any of the listed tags, and the two ingestion-date bounds are inclusive ISO timestamps.

An explicit "document_ids": [] is an active scope that matches nothing. It does NOT mean unscoped. If you build the array dynamically and it comes out empty, the ask will answer from zero documents and honestly report that nothing matched, which reads as a confusing empty answer if you expected the whole corpus. Omit the field entirely to leave the turn unscoped. This strictness is deliberate: it is the same property that guarantees a scope can never silently widen.

Scope fields (all optional, ANDed when present)

document_idsuuid[]Restrict to these documents. Capped at 1,000 ids. An empty array matches nothing.
schema_iduuidRestrict to documents processed by a pipeline of this Spec.
pipeline_iduuidRestrict to documents attached to this pipeline run.
data_product_iduuidRestrict to the documents behind this data product's pipeline run.
document_typestringRestrict to documents of this classification (a document type name, max 200 chars).
source_iduuidRestrict to documents ingested through this source connection.
tagsstring[]Restrict to documents carrying ANY of these user tags. Capped at 100 entries.
ingested_afteriso date-timeRestrict to documents ingested at or after this timestamp.
ingested_beforeiso date-timeRestrict to documents ingested at or before this timestamp.

A scoped ask

Model selection

Two fields choose the model, and the resolution order is explicit: an explicit model wins, then model_class, then the workspace AI policy binding for the operation, then the platform default. model names a specific model, for example claude-opus; model_class names a capability class, for example complex, and applies only when model is absent. An unknown model_class is rejected with 400 listing the known classes.

An explicitly named model is never silently substituted. Model admissibility is decided by the workspace [AI policy](get-ai-policy): if the policy denies the model you named, through a denial list, a provider constraint, or a region pin, the request fails loudly rather than running on something you did not ask for. This is the same fail-loud contract the policy applies platform-wide, and [GET /v1/ai/models](ai-models-operations) tells you in advance which models are reachable under the live policy, with the denial reason for each one that is not.

Pinning the model

Output format

The output_format object is a formatting contract for the final answer: instruction is a free-text response-format instruction (up to 4,000 characters), and template is a markdown skeleton (up to 20,000 characters) whose headings and order the answer keeps. Use it to make answers land directly in your rendering surface: a fixed report layout, a table with named columns, a JSON shape inside a code fence. It constrains the shape of the response only. Grounding, citation, and the refusal to state unsupported claims are not negotiable through it, so a template cannot talk the agent out of citing its sources.

on_behalf_of and compartments

By default, a workspace API key reads across every compartment and classification in the workspace, including documents that a human viewer in the same workspace could not open. on_behalf_of opts a single request into compartment-correct retrieval by naming a user in the calling workspace: the turn then retrieves only what that user may see. This is opt-in per request and controlled by the caller; it is not enforced by the key. If your workspace uses compartments to separate what people may see, an integration that forwards answers to end users should pass on_behalf_of on every request, because an ask without it answers with workspace-wide visibility.

The named user must be an active member of the calling workspace; anything else is rejected with 400 rather than silently falling back to the unscoped default, because falling back would answer a compartment-correct question with a compartment-blind answer. on_behalf_of also partitions conversations: turns run for a user thread into that user's own conversation partition, so one user's history is never replayed as context into a turn run for another. The same field, with the same semantics, exists on [POST /v1/agent/tools/:name/invoke](agent-invoke-tool) and as a query parameter on the [Conversations](agent-conversations) endpoints.

Compartment-correct ask