Appearance
Agents
Create, publish, share, and invoke named agents.
- Management endpoints under
/v1/core/...operate on agents in the authenticated user's account. - Routes under
/v1/public/agent/:username/:slug...are still authenticated in the current API. Access is limited to the owner or an explicitly shared recipient. - Shared agents can also be invoked through
POST /v1/chat/completionsby passingmodel: "shared/<ownerUsername>/<slug>".
Endpoints
GET /v1/core/agents
Returns your agent configurations (map keyed by agent name).
ts
await fetch("https://agents.missionsquad.ai/v1/core/agents", {
headers: { "x-api-key": process.env.MSQ_API_KEY! }
});Response shape:
ts
{
data: {
[agentName: string]: {
type: "user-agent";
config: Record<string, unknown>;
};
};
}POST /v1/core/add/agent
Create or update an agent.
Body (required + optional fields supported by the server). The body is Omit<NewAgentConfig, "modelId"> plus the override fields below; model is the model name (resolved to a model id server-side):
ts
{
name: string; // required
description: string; // required
systemPrompt: string; // required (or supply systemPromptId)
systemPromptId?: string; // a promptId from POST /v1/core/generate/prompt; resolved to systemPrompt
model: string; // required; name of a model you've added (or its id)
overwrite?: boolean;
previousName?: string; // if renaming, migrates references in workflows
icon?: string;
addToday?: boolean; // if true and first message is `system`, current date is injected
timezoneOffset?: string; // for date formatting; defaults to "-0500" if not provided
tools?: string[]; // MCP function names; resolved to selectedFunctions automatically
selectedFunctions?: { // or preselect MCP functions per server explicitly
[serverName: string]: string[];
};
modelOptions?: { temperature?: number; maxTokens?: number };
embeddingOptions?: {
model: string;
collections: string[];
type: string;
resultCount?: number; // total RAG passages, integer 1–100; omit for legacy defaults
};
voice?: { // attach a saved voice for /speak
savedVoiceName: string;
responseFormat?: "mp3" | "opus" | "aac" | "flac" | "wav" | "pcm";
speed?: number;
ttsOptions?: Record<string, unknown>;
};
responseProcessorAgent?: string;
}Status codes: 200 { status: "success", message, data }; 400 for missing required fields or an expired systemPromptId or invalid embeddingOptions.resultCount; 404 when model cannot be resolved; 409 when the agent already exists and overwrite is not set.
embeddingOptions.resultCount limits the total number of document passages included in each RAG retrieval across all selected collections. Candidates are ordered by ascending distance before selection, so a more relevant passage in a later collection can displace one in an earlier collection. The Document search event contains the selected passages in the same order as the model's context; totalCandidates counts returned candidates before filtering and selection. Fewer passages can be returned when there are not enough usable results.
Omit resultCount to preserve the legacy defaults: 5 for agent calls; 10 for Missions. Valid values are JSON integer numbers from 1 through 100, inclusive. Strings such as "20", null, booleans, fractions, zero, negative values, and values above 100 return 400 before configuration changes. The count is independent of model sampling topK and the manual Embeddings search limit. It does not guarantee that the selected passages fit every model's context window.
For example, include this object when creating or saving a RAG agent:
json
{
"embeddingOptions": {
"model": "embedding-model-id",
"collections": ["manuals", "support"],
"type": "open",
"resultCount": 20
}
}To clear an explicit count, supply the embedding options with resultCount omitted. Do not send null or zero. Omitted defaults are not written into saved configurations. A saved change applies to subsequent retrievals; historical prompts/events and existing Mission agent snapshots are not rewritten.
Programmatic tool calling for Claude is configured on the model, not the agent — see Models. An agent inherits it from its model.
Example:
ts
await fetch("https://agents.missionsquad.ai/v1/core/add/agent", {
method: "POST",
headers: { "x-api-key": process.env.MSQ_API_KEY!, "Content-Type": "application/json" },
body: JSON.stringify({
name: "my-custom-agent",
description: "Specialized helper",
systemPrompt: "You are helpful.",
model: "my-gpt4",
overwrite: false,
addToday: true,
timezoneOffset: "-0500"
})
});POST /v1/core/delete/agent
Delete an agent by name.
Body:
json
{ "name": "my-agent" }ts
await fetch("https://agents.missionsquad.ai/v1/core/delete/agent", {
method: "POST",
headers: { "x-api-key": process.env.MSQ_API_KEY!, "Content-Type": "application/json" },
body: JSON.stringify({ name: "my-agent" })
});PUT /v1/core/update/agent
Update an existing agent by name. Omitted fields keep their current values. The protected utility agents title-agent and msq-config-agent cannot be modified (403).
This legacy partial-update endpoint preserves existing embeddingOptions, including an explicit resultCount. To author or clear RAG settings, use POST /v1/core/add/agent with the complete agent configuration and overwrite: true.
Body:
ts
{
name: string; // required; identifies the agent
description?: string;
icon?: string;
systemPrompt?: string;
systemPromptId?: string;
model?: string; // model name; defaults to the existing model when omitted
addToday?: boolean;
timezoneOffset?: string;
tools?: string[];
selectedFunctions?: { [serverName: string]: string[] };
modelOptions?: { temperature?: number; maxTokens?: number };
temperature?: number; // deprecated; use modelOptions.temperature
maxTokens?: number; // deprecated; use modelOptions.maxTokens
combineSystemPrompts?: boolean;
convertSystemPrompt?: boolean;
}Status codes: 200 { status: "success", message, data }; 400 missing name / expired systemPromptId; 403 protected utility agent; 404 agent or model not found.
POST /v1/core/agent/speak
Run an owned agent and stream text deltas plus synthesized audio (TTS) as SSE. The agent must have a saved voice configured.
Request body:
ts
{
name: string; // agent name
messages: Array<{ role: "system" | "user" | "assistant" | "tool"; content: string | null; /* ... */ }>;
processorAgentName?: string; // optional response-processor agent
ttsOverrides?: {
responseFormat?: "mp3" | "opus" | "aac" | "flac" | "wav" | "pcm"; // default "opus"
speed?: number;
ttsOptions?: Record<string, unknown>;
};
}Streaming response (Content-Type: text/event-stream):
data: {"type":"status","stage":"generating","time":<ms>}(initial + 10s keepalive)data: {"type":"text_delta","content":"..."}data: {"type":"message_stop"}data: {"type":"audio_chunk","b64":"..."}data: {"type":"audio_stop"}data: {"type":"error","message":"..."}data: [DONE]
Pre-stream errors: 400 (missing name/messages), 404 (agent not found).
POST /v1/core/agents/publish
Toggle an owned agent's published state. Publishing creates or updates a stable public slug derived from agentName.
Request body:
ts
{
agentId: string;
agentName: string;
}Response body:
ts
{
success: true;
data: {
userId: string;
username: string;
agentId: string;
agentName: string;
slug: string;
isPublished: boolean;
publishedAt: number;
updatedAt: number;
};
}Important error cases:
403if the account does not have agent publishing enabled.404if the agent does not exist in the caller's account.409if a conflicting publish record already exists.429witherror: "LIMIT_EXCEEDED"when the account has reachedmaxPublishedAgents.
GET /v1/core/agents/published
List publish records for the authenticated user.
Response body:
ts
{
success: true;
data: Array<{
userId: string;
username: string;
agentId: string;
agentName: string;
slug: string;
isPublished: boolean;
publishedAt: number;
updatedAt: number;
}>;
}GET /v1/core/agents/shared-with-me
List agents that were explicitly shared with the authenticated user by username or email.
Response body:
ts
{
success: true;
data: Array<{
agentId: string;
agentName: string;
icon?: string;
slug: string;
description: string;
isPublished: boolean;
publishedAt: number;
ownerUsername: string;
sharedAt: number;
sharedVia: "username" | "email";
hasVoice: boolean;
editable: boolean; // true when the owner shared this agent with edit access
modelName: string; // friendly display name of the agent's model
toolsCount: number; // number of MCP functions/tools the agent uses
}>;
}editable: true means you may edit a curated subset of the agent's config via the shared-agent config endpoints below.
To invoke one of these via POST /v1/chat/completions, construct the shared model id as:
ts
const model = `shared/${ownerUsername}/${slug}`;POST /v1/core/agents/:username/:slug/shares
Create or refresh a share record for a published agent. :username must match the authenticated owner. Re-issuing this call for the same recipient updates the editable flag (there is no separate "make editable" endpoint).
Request body:
ts
{
recipient: string; // username or email
editable?: boolean; // when strictly true, the recipient may edit a curated subset of fields
}Response body:
ts
{
success: true;
}Important error cases:
403if the caller is not the owner named in:username.404if the published agent does not exist or the username recipient does not exist.
GET /v1/core/agents/:username/:slug/shares
List the normalized recipient entries for a published agent. :username must match the authenticated owner.
Response body:
ts
{
success: true;
data: Array<{
recipientType: "username" | "email";
recipient: string; // normalized lowercase username or email
createdAt: number;
editable: boolean;
}>;
}DELETE /v1/core/agents/:username/:slug/shares
Revoke a share for a published agent. :username must match the authenticated owner.
Request body:
ts
{
recipientType: "username" | "email";
recipient: string;
}Response body:
ts
{
success: boolean;
}Editing a shared agent (collaborative editing)
When an owner shares an agent with editable: true, the recipient may edit a curated subset of the agent's configuration. Edits are written to the owner's agent config. All four endpoints below are gated by enforceSharedAgentEditAccess: access is allowed for the owner, or for a recipient who holds an editable share. Unauthenticated callers get 401 (code: "AUTH_REQUIRED"); non-editable callers get 403 (code: "OWNER_OR_SHARED_EDIT_ONLY").
GET /v1/core/agents/:username/:slug/config
Fetch the owner's full agent config for editing.
Response body:
ts
{
success: true;
data: AgentConfig; // the owner's full agent configuration
modelName: string; // friendly model display name
}PUT /v1/core/agents/:username/:slug/config
Update the shared agent's config. The model is never changed here (it stays the owner's model), and non-owners cannot send name, model, modelId, responseProcessorAgent, temperature, or maxTokens (those return 400 code: "FIELD_NOT_EDITABLE"). Referenced tools, embedding collections, and voices must belong to the owner (validated against the owner-resource endpoints below; mismatches return 400 code: "INVALID_OWNER_RESOURCE").
Request body (all optional):
ts
{
description?: string;
icon?: string | null;
systemPrompt?: string;
systemPromptId?: string;
modelOptions?: { temperature?: number; maxTokens?: number };
addToday?: boolean;
timezoneOffset?: string;
combineSystemPrompts?: boolean;
convertSystemPrompt?: boolean;
toolOptions?: {
systemTools?: string[];
userTools?: string[];
selectedFunctions?: { [serverName: string]: string[] };
};
embeddingOptions?: { model: string; collections: string[]; type: string; resultCount?: number };
voice?: {
savedVoiceName?: string | null;
responseFormat?: "mp3" | "opus" | "aac" | "flac" | "wav" | "pcm";
speed?: number;
ttsOptions?: Record<string, unknown>;
} | null;
}Response body: { status: "success", message: string, data: AgentConfig }.
For shared updates, omitting the entire embeddingOptions object preserves the current options and any explicit count. Supplying an options object replaces those options: omit its resultCount to clear a previously chosen count and restore the path-specific defaults (5 for agent calls; 10 for Missions). Explicit counts use the same integer 1–100 validation as ordinary saves, with invalid values returning 400 before changes are saved. Owner-resource and shared-edit access checks still apply.
GET /v1/core/agents/:username/:slug/owner-tools
List the owner's MCP servers and tools so an editor can pick valid toolOptions. Returns a credential-free projection:
ts
{
success: true;
servers: Array<{ name: string; displayName: string; description: string; enabled: boolean; installed: boolean; source: string }>;
tools: unknown; // the owner's tools, grouped by server
}GET /v1/core/agents/:username/:slug/owner-embeddings
List the owner's vector stores and embedding models for picking valid embeddingOptions:
ts
{
success: true;
vectorStores: Array<{ name: string; safeName: string; embeddingModel: string }>;
embeddingModels: Array<{ id: string; name: string; modelName: string; providerKey: string }>;
}GET /v1/core/agents/:username/:slug/owner-voices
List the owner's saved voices for picking a valid voice.savedVoiceName:
ts
{
success: true;
voices: Array<{ id: string; name: string; providerKey: string; responseFormat: string; speed: number }>;
}GET /v1/public/agent/:username/:slug
Fetch the public-safe metadata for a published or shared agent. The caller must be the owner or an explicitly shared viewer.
Response body:
ts
{
success: true;
data: {
name: string;
description?: string;
hasVoice: boolean;
settings: {
titleAgentName: string | null;
};
};
}Important error cases:
401withcode: "AUTH_REQUIRED"when the request is unauthenticated.403withcode: "OWNER_OR_SHARED_ONLY"when the caller does not have access.404when the published agent or backing agent configuration does not exist.
POST /v1/public/agent/:username/:slug/chat
Chat with a published/shared agent using the owner's model, tool, and agent configuration.
Request body:
ts
{
messages: Array<{
role: "system" | "user" | "assistant" | "tool";
content: string | null;
name?: string;
tool_calls?: Array<{
id?: string;
type: string;
function: {
name: string;
arguments: string;
};
}>;
tool_call_id?: string;
providerState?: Record<string, unknown>;
rawContentBlocks?: unknown[];
}>;
stream?: boolean;
sessionId?: string;
chatId?: string;
requestTimeoutMs?: number; // integer, 1-1800000
maxRetries?: number; // integer, 0-10
}Non-stream response:
ts
// JSON completion payload returned by the agent execution path.
// In MissionSquad API deployments this is the same completion object family
// used by the rest of the chat API.
Record<string, unknown>Streaming response:
Content-Type: text/event-stream- Response header
X-Run-Id: <runId> data: {"object":"chat.completion.chunk",...}OpenAI-style content chunksdata: {"type":"status","stage":"generating","time":<unix-ms>}keepalive/status events before first content and during idle periodsdata: {"object":"chat.completion.context","messages":[...],"providerState":...}final persisted-context eventdata: {"object":"chat.completion.chunk","usage":{...}}final usage chunk when token data is availabledata: [DONE]
POST /v1/public/agent/:username/:slug/speak
Run the agent and receive text plus synthesized audio as SSE.
Request body:
ts
{
messages: Array<{
role: "system" | "user" | "assistant" | "tool";
content: string | null;
name?: string;
tool_calls?: Array<{
id?: string;
type: string;
function: {
name: string;
arguments: string;
};
}>;
tool_call_id?: string;
providerState?: Record<string, unknown>;
rawContentBlocks?: unknown[];
}>;
chatId?: string;
processorAgentName?: string;
sessionId?: string;
ttsOverrides?: {
responseFormat?: "mp3" | "opus" | "aac" | "flac" | "wav" | "pcm";
speed?: number;
ttsOptions?: Record<string, unknown>;
};
}Streaming response:
Content-Type: text/event-stream- Response header
X-Run-Id: <runId> data: {"type":"run","runId":"..."}data: {"type":"status","stage":"generating"|"audio","time":<unix-ms>}data: {"type":"text_delta","content":"..."}data: {"type":"message_stop"}data: {"type":"audio_chunk","b64":"..."}data: {"type":"audio_stop"}data: {"type":"usage","promptTokens":number,"completionTokens":number,"totalTokens":number}data: {"type":"error","message":"..."}data: [DONE]
GET /v1/public/agent/:username/:slug/sessions
List saved chat sessions for the authenticated viewer and the specified published/shared agent.
Query parameters:
ts
{
limit?: number; // default 50
offset?: number; // default 0
}Response body:
ts
{
success: true;
data: PublicChatSessionRecord[];
}GET /v1/public/agent/:username/:slug/sessions/:id
Fetch one saved public-agent chat session for the authenticated viewer.
Response body:
ts
{
success: true;
data: PublicChatSessionRecord;
}POST /v1/public/agent/:username/:slug/sessions
Create or replace a saved chat session for the authenticated viewer. The server injects userId, agentUsername, and agentSlug from auth and route params before validating the record.
Request body:
ts
{
id: string;
title: string;
createdAt: number;
updatedAt: number;
messages: ModelMessageRecord[];
providerState?: Record<string, unknown>;
}Response body:
ts
{
success: true;
}PATCH /v1/public/agent/:username/:slug/sessions/:id/title
Update only the saved session title.
Request body:
ts
{
title: string;
updatedAt?: number;
}Response body:
ts
{
success: true;
title: string;
updatedAt: number;
}DELETE /v1/public/agent/:username/:slug/sessions/:id
Delete a saved session for the authenticated viewer.
Response body:
ts
{
success: boolean;
message: "Deleted" | "Not found";
}Shared record types used by the session endpoints:
ts
type ModelMessageRecord = {
role: string;
content: string | null | Array<
| { type: "text"; text: string }
| { type: "image"; image: { mimeType: string; base64Data: string } }
>;
name?: string;
tool_calls?: Array<{
id?: string;
type: string;
function: { name: string; arguments: string };
}>;
tool_call_id?: string;
providerState?: Record<string, unknown>;
rawContentBlocks?: unknown[];
tokenUsage?: {
promptTokens?: number;
completionTokens?: number;
totalTokens?: number;
};
toolEvents?: Array<{
type:
| "tool_start"
| "tool_end"
| "tool_error"
| "tool_warning"
| "llm_start"
| "llm_end"
| "llm_chunk";
message?: string;
data: Record<string, unknown>;
sessionId?: string;
clientId?: string;
timestamp?: number;
}>;
};
type PublicChatSessionRecord = {
id: string;
userId: string;
agentUsername: string;
agentSlug: string;
title: string;
createdAt: number;
updatedAt: number;
messages: ModelMessageRecord[];
providerState?: Record<string, unknown>;
};Notes
- Agents can be invoked from chat completions by setting
modelto the agent'sname(e.g.,model: "my-custom-agent"). - Share-management endpoints require the authenticated owner to use their own username in
:username. - Routes under
/v1/public/agent/:username/:slug...are access-checked, not anonymous. A valid API key and explicit owner/share access are required.