Relay docs

Agents

An agent is a reusable, callable configuration: a model + a system prompt + generation defaults, optionally grounded on a knowledge base, a dataset, a user's own Microsoft 365 account or the live web, and equipped with skills, tools, MCP servers and workflows. Apps invoke it by id and get the full behaviour without re-assembling prompts each time.

#What an agent bundles

PartPurpose
ModelThe public alias the agent runs on.
System promptEither free-text, or a reference to a library prompt (resolved production → latest at run time).
SkillsReusable instruction blocks folded into the system prompt.
Tools / MCP serversAdvertised to the model; MCP-backed tools are executed by Relay, plain registry tools are returned to the caller.
WorkflowsWorkflows attached as tools: called by the model like functions and run by Relay as child runs.
Knowledge baseOptional RAG grounding — retrieved chunks are injected with [S#] citations.
DatasetOptional structured-data grounding — the agent writes SQL and Relay runs it. See Dataset agents.
ConnectorOptional read-only access to the acting user's own Microsoft 365 mail, calendar and files. See Connectors.
Web searchOptional internet search during a run, with the pages the answer cites. Claude models only. See below.
Generation defaultsTemperature, max tokens, retrieval top-K.
GuardrailsOptional PII redaction before text goes upstream (also applied to what the agent's traces capture).
Trace captureThe agent's trace-body capture level, overriding the key's; callers can lower it, never raise it. See Observability.
AudiencePublic (the default), or shared with named users — plus its creator, always. See below.

#Who can run an agent

Two rules apply in order, and both must pass.

First, workspace: an agent is reachable by an API key when the agent is global (no workspace) or belongs to the key's workspace. That rule is unchanged and applies to every agent.

Second, audience. An agent is either public — any caller whose key can see it may run it, which is how every agent behaves by default — or shared with a specific set of users. A shared agent is runnable by its creator and by the users named on it, and the caller has to say which user it is acting for by sending the X-Relay-User-Id header. Without that header the run is refused with 400 user_header_required; with a user who is neither the creator nor on the list, 403 agent_not_shared. GET /v1/agents simply omits the agents the named user may not run, so a client can list and offer only what will actually work.

The creator always keeps access. Whoever created an agent can run it whatever its sharing says, and does not appear in — or need to be added to — the share list. This is why a private agent with nobody on it is still useful: it is a draft only its author can run. The owner is stamped once, server-side, from the signed-in panel user; it is never read from a request, or granting yourself permanent access to someone else's agent would be a matter of posting the right field.

Set this in the panel under Agents: open the agent's editor, switch Access to Specific users, and pick the people from the list.

#Which id a user is

The id an agent is shared by is the user's Entra object id (oid) — the same value the caller puts in X-Relay-User-Id, and the same one Relay forwards to the data app for a dataset agent's row-level security and uses to pick a connector connection. That is what makes one header enough for an agent that is private, dataset-backed and connected.

It is deliberately not the panel's own application_user.id, which is a separate id ASP.NET Identity generates; the two are unrelated values for the same person. The panel's picker resolves the object id for you, so this only matters if you are writing share rows by some other route — in which case note the failure mode, because it is a quiet one: a share keyed by the wrong id saves successfully and then matches nobody, and the agent simply appears to be shared with no one.

A user who has never signed in with Microsoft has no object id at all, so there is nothing to match them by. The picker lists them greyed out rather than hiding them, so the reason is visible.

Two things worth knowing:

  • The header is caller-asserted. Relay trusts the API key holder to name its end user, exactly as it already does for dataset agents. This controls which agent a trusted application may run on someone's behalf; it does not authenticate that person, and the application is still responsible for signing its own users in.
  • Sharing is live, not versioned. A run resolves the audience from the current agent, before any published snapshot is applied. Making an agent private takes effect immediately, and publishing an older version never brings back an older audience.

An agent that is private with nobody on it is runnable by its creator alone. The panel labels that owner only rather than treating it as an error — it is exactly the state you want while building one. The exception is an agent created before owners were recorded: it has no creator, so private and unshared means nobody at all can run it, and the panel labels it nobody.

#Prompt source & variables

When creating an agent you either write a system prompt or select a library prompt. If the prompt has {{variables}}, callers supply them per run and Relay compiles the prompt before executing — pass a top-level variables object (see the Agents API). A chat-type prompt contributes its user/assistant turns to the conversation, so the caller's message can even be omitted.

#Composing the request

At run time the agent builds:

system   = skills + prompt system text + [connector note] + [dataset schema] + retrieved KB context + [attached files]
messages = prompt seed turns + thread history + the caller's message

The caller's own system message, if any, comes after the agent's. If there is no user or assistant turn anywhere, the system prompt is promoted to the user message (or, with no prompt at all, a "Follow your instructions and respond." turn is added), so every provider runs. Attached files come from the request's file_ids — see File attachments.

Turn on Search the web in the editor's Tools section and the model can look things up on the internet while it answers. Relay sends Anthropic's server-side web search tool (web_search_20250305) with the agent's model call, Anthropic runs the searches, and the answer comes back citing the pages it used. There is no search vendor or extra key to set up.

Claude models only. The searches run inside Anthropic's API, so the agent's model must be served by an Anthropic provider or by Azure AI Foundry's Anthropic route (/anthropic/…). With any other model, including an auto routing directive, a run is refused with 400 web_search_unsupported rather than answered without searching, and the editor warns you before you save. A fallback to a non-Claude model drops the search, as it does for any Anthropic-only field.

The settings:

SettingEffect
Searches per callThe most searches one model call may make, 1 to 20. Defaults to 5. Past the limit the model answers from what it has.
SitesWhole web, Only these (search is restricted to the listed domains) or Never these (results from the listed domains are excluded). One domain per line; subdomains are included. Anthropic accepts one list or the other, not both.

Things worth knowing:

  • It acts as nobody. Web search reads the public web, not a person's account, so it needs no X-Relay-User-Id, no extra scope, and it runs from a workflow's Agent node.
  • What it found is returned. A non-streaming run gains a relay_web_search object with the queries, the pages found, the citations and the number of billed searches, summed across every model call in a tool loop. A streamed run returns only the text. The agent's trace records the search count and queries.
  • The agent's settings win. A caller's own anthropic_tools web search or fetch entry is replaced by the agent's, so a caller cannot widen a site restriction.
  • Never cached. A call that carries web search skips the response cache, because yesterday's search is the wrong answer to today's question.
  • Cost. Anthropic bills each search on top of tokens, and that charge is not yet included in X-Relay-Cost-Usd or the dashboards.
  • Content leaves for the search. The model writes the search queries from the conversation, so turn on Redact PII if questions may carry personal data you do not want in a query.

#Versions & deployment

Agents are versioned like prompts. Snapshot the current definition, publish a version to deploy it, or restore an old version into the draft. The run endpoint prefers the published snapshot over live draft edits — so you can edit safely and deploy deliberately. Pinning a dataset or attaching a connector to an agent with a published version therefore does nothing until you publish again.

Restore copies the model, written instructions, knowledge base, generation defaults, PII setting, skills, tools, MCP servers, workflow tools and web search settings into the draft. The library prompt reference, dataset, connectors, trace-capture level and enabled flag are not restored — and audience never is, since access is always the live setting.

In the panel this is the agent's Versions tab: the draft and every snapshot sit in a timeline on the left (with its note, age and author), and the selected one shows its configuration as readable lines, a diff against the published version or any other, and the raw snapshot JSON.

#Running an agent

  • API: GET /v1/agents to discover them, POST /v1/agents/{id}/chat/completions to run one — see Agents API.
  • Voice: point an agent at a realtime model and it can be spoken to over a WebSocket, with its written prompt, skills, registry tools and knowledge base applied — see Realtime for what does not carry over.
  • From a workflow: the Agent node runs an agent as one step. It refuses private, dataset and connector agents, because a workflow run has no acting end user and no API key to check scopes against — see Workflows. Web search works there, and the node's output gains a webSearch object with the queries, search count and cited sources.
  • Panel: each agent has its own page. Overview shows its instructions (a library prompt is shown as the version it will actually serve), tools, knowledge, configuration, who can run it, and what a caller must send. Run is a multi-turn console that calls the agent through a picked API key (the panel never sees the secret) with its variables and acting user, and shows any SQL a dataset agent ran. Traces lists its recent runs and Use agent has cURL, Python and JavaScript snippets. Open in Playground opens the Playground with the agent already selected.
  • Editing: creating, editing and duplicating (?from=) happen on one full page with a section for each concern — basics, instructions, knowledge & data, tools, generation & safety, and access. Saving changes the draft only, so when a version is published callers keep getting it until you publish again.
  • Copying to another workspace: Copy to workspace… on the Agents list copies the live draft into another workspace you are an active member of. Every workspace-owned reference — library prompt, knowledge base, skills, tools, MCP servers, workflow tools, connectors — is relinked to the same-named item in the target or dropped and reported; the dataset reference is kept; you become the owner; public/private is kept but the share list and version history are not. The dialog lists everything that was not carried as-is.
  • Memory: add ?thread=<id> for multi-turn conversations.