# Create a model response

Creates a model response for AI-powered text generation, structured outputs, and tool-augmented workflows.

Provide text or image inputs to generate text or JSON outputs. Have the model call your own custom code or use built-in tools like web search or file search.

#### Request Body

### Core Parameters

**`organization_id`** string optional

The unique identifier of the organization to which this request belongs. All API requests must be scoped to an organization for billing, access control, and resource management. Find your organization ID in [Freddy → Settings → Organization](https://chat.aitronos.com/settings/organization).

**`assistant_id`** string optional

ID of the assistant to use for this response. When provided, automatically applies the assistant's configured rules, instructions, and settings. Enables consistent AI behavior across conversations with priority-based rule application and context management. [Learn more about assistants](/docs/documentation/core-concepts/assistants)

**`disable_rules`** boolean optional · Defaults to `false`

Disable all rule application for this response. When `true`, no rules will be applied regardless of other rule settings. This parameter is only available when using API keys and cannot be used with bearer tokens (user authentication). [Learn more about rules](/docs/documentation/core-concepts/rules)

**`skill_ids`** array optional

Target a specific set of skills for this single request. When provided, ONLY these skills are injected into the system prompt — overrides both the user's full library and any skills attached to the assistant. Accepts personal skill IDs (`skill_...`), organization skill IDs (`skill_...`), and platform skill names (`platform-...`). Skills the user cannot access are silently skipped. Pass an empty list to inject no skills. Unset/null preserves the default resolution (assistant attachments → full library).

**`style_notes`** array optional

This-turn delivery chips. Each entry is `{ "id": "sty_…", "intensity": "subtle"|"normal"|"strong" }` or `{ "slug": "concise", "intensity": "subtle" }`. Compiled onto the user turn after the datetime line so they do not bust the assistant prompt cache. Empty/omit = none. Max 8 (served as `max_items` on the catalog). Several notes blend into one voice; where two conflict, the earlier one wins. A note named twice (same id, same slug, or an id and a slug for one note) applies once, at its first position. Requires the `style_notes` beta feature switch; a non-empty list while the switch is off returns 422 `FEATURE_NOT_ENABLED`, and 503 `STYLE_NOTES_SWITCH_UNRESOLVED` (retryable) when the switch cannot be read. Unknown or inaccessible notes return 422 `INVALID_FIELD_VALUE`. Catalog: [List style profiles](/docs/api-reference/style-profiles/list) with `kind=note` (404 `FEATURE_NOT_ENABLED` when the switch is off). Playground-only `personality_id` / `personality_settings` on this request are ignored when `assistant_id` is set.

**`model`** string optional · Defaults to `ftg-3.0`

The AI model to use for generating the response. Aitronos supports various models with different capabilities, performance characteristics, and pricing. Choose based on your task requirements: reasoning models for complex problems, fast models for simple queries, or vision models for image understanding. [View available models →](/docs/documentation/getting-started/models)

**`auto_generate_name`** boolean optional

When `true`, the backend auto-generates a concise title for the thread after the turn completes — but only while the thread is still unnamed (it never overwrites a user-set title).

**`usage_context`** object optional

Attribution context for billing. Lets a usage/cost line be traced back to its product origin (source), sub-feature, AI mode, and the entity/field it acted on.

**`voice_lean`** boolean optional

Enable low-latency lean mode. Trims the system prompt to its essentials and keeps only a few core tools loaded up front (the rest stay reachable on demand) to reduce time-to-first-token. Off by default.

**`inputs`** array required

Array of input message objects. Each message contains a role and content (text string or structured content blocks), with optional file attachments. [View full object →](/docs/api-reference/objects/input-message)

## Properties

**`role`** string required

The role of the message input. One of `user`, `system`, or `assistant`.

**`content`** string | array required

The message content. Accepts either a plain text string or an array of content blocks.

details
summary
Show content block structure
Each content block:

**`type`** string required

Content type: `text` or `file`.

**`text`** string optional

Text content. Required when `type` is `text`.

**`file_id`** string optional

File ID reference (`file_` prefix). Required when `type` is `file`.

**`files`** array optional

Array of file references for context retrieval. Upload files first via the [Files API](/docs/api-reference/files/upload), then reference them here by `file_id`. The system retrieves relevant content from documents and injects it into the conversation. Images (PNG, JPEG, WebP) are sent as visual input to vision-capable models. Maximum 10 files per message.

details
summary
Show structure
Each file reference:

**`file_id`** string required

ID of an uploaded file (e.g., `file_abc123def456abc123def456abc12345`).

Example: `[{ "file_id": "file_abc123def456abc123def456abc12345" }]`

**`id`** string optional

The unique ID of the input message. Populated when items are returned via API.

### Thread and Context

**`thread_id`** string optional

The ID of the thread that this response belongs to. Items from this thread are prepended to inputs for this response request. Input items and output items from this response are automatically added to this thread after completion. [Learn more](/docs/documentation/core-concepts/threads)

If no `thread_id` is provided, a new thread is automatically created for this request. The response will include the new thread ID, which can be used in subsequent requests to maintain conversation context and history. This enables seamless multi-turn conversations without manual thread management. For stateless multi-turn conversations, use `previous_response_id` instead.

**`store`** boolean optional · Defaults to `true`

Controls whether the created thread is visible in the user's thread list. When `true` (default), the thread appears in the list threads API and is visible in the user interface. When `false`, the thread is hidden from the list threads API but remains accessible by direct thread ID lookup. This is useful for internal operations, background tasks, or temporary threads that shouldn't clutter the user's conversation history. [Learn more](/docs/documentation/core-concepts/threads#thread-visibility)

**`space_id`** string optional

ID of the space to associate the thread with. When provided, the response searches the specified space: its dedicated vector store and any additional attached vector stores are merged into the conversation's knowledge sources, enabling shared context across all threads in the space. The thread is also added to the space when the caller could add it with [Add thread to space](/assets/add-thread.b7574a15646f0362e5241b5d034df5fb8c81104e9463205e535c8614f70972b0.a2c6176c.md): a thread you own needs view access to the space, a thread with no owner or a request made with an organization API key needs edit access, and a thread owned by someone else is never added. An incognito thread is never added. When the thread cannot be added, the request does not fail and its knowledge search stays limited to that space. This is idempotent — passing the same `space_id` on subsequent messages to the same thread has no effect. [Learn more about spaces](/assets/list-threads.64fea4fa4bd576d911418ac963d42875578260875488ac23b9795162f3d5ca80.a2c6176c.md)

**`instructions`** string optional

System-level instructions that define the model's behavior and capabilities. These instructions override any previous system messages when continuing from a prior response, allowing you to dynamically adjust the model's context without carrying over old system prompts.

### Response Delivery

**`stream`** boolean optional · Defaults to `false`

Enable streaming mode to receive response data incrementally as it's generated, using server-sent events (SSE). When enabled, the model sends partial outputs in real-time rather than waiting for the complete response. Ideal for user-facing applications requiring immediate feedback. [Learn more](/docs/documentation/running-methods/streaming-mode)

**`activity_labels`** boolean optional · Defaults to `false`

Opt in to activity labels on the streamed event feed. When `true`, [streaming events](/docs/api-reference/objects/streaming-event-object) may carry an additional `activity` field — a short, human-readable note describing what the turn is doing at that moment, suitable for showing as a live status line. When omitted or `false`, no event carries the field and the stream is byte-identical to a request made before this parameter existed.

Has no effect when `stream` is `false`: activity labels ride the streamed event feed only, so a non-streaming response never contains one. Treat the field as always optional even when you ask for it — write clients to render it when present and to behave normally when it is absent.

Every streamed turn echoes the **resolved** state back on its [`response.created`](/docs/api-reference/objects/streaming-event-object) event as a top-level `activity_labels` boolean, exactly once. `true` means labels are active for this turn and events may carry `activity`; `false` means they are not — whether because the request did not ask or because the deployment has labels disabled. The echo is present on every `response.created`, including for requests that never set the parameter, so `false` and "this backend predates the echo" (the field absent entirely) remain distinguishable. Use it for diagnostics — it tells you in one frame whether a missing `activity` field is expected — and never as a gate on rendering, since absence of `activity` is always normal.

**`locale`** string optional

BCP-47 language tag for the `activity` labels, e.g. `de-CH`, `fr-CH` or `en`. Takes precedence over the assistant's own `locale`. When neither names a supported language, the assistant's Auto setting decides: the language of the person's message, then the language they chose for their account, then English. Supported: `en-GB`, `de-DE`, `de-CH`, `de-AT`, `de-AT-x-ziller` (Zillertal German, worded exactly as `de-AT`), `fr-CH` and `es-ES`. Another region reads its language (`en-US` as English), and German without one of its regions (`de`, `de-LU`) takes the region of the person's own language, else `de-CH`. English and German labels use the house vocabulary; French and Spanish labels are plain, literal wording. Each label's `activity.locale` names the tag it is written in. POSIX forms such as `de_AT.UTF-8` are accepted. A tag for any other language, or a value that is not a language tag at all, is ignored (as if absent) and never fails the request. The `Accept-Language` header is not consulted. Affects only the progress labels, never the model's answer, and only when `activity_labels` is `true`.

**`motion`** object optional

Ask this turn for one motion: an animated Smartefact in scenes, saved by the assistant and, when you ask, exported as a video. Omit it for an ordinary turn; a request without it is answered exactly as before. Requires the organization's motion switch: when it is off, the request is refused before any model call with `404 FEATURE_NOT_ENABLED` (`details.feature: smoc_motion_beta`), and `503` when the switch could not be read. While the motion is written the stream sends one [`response.smac.motion.scene`](/assets/streaming-events.bba0eb7ccc6ef01fae9e6e34446a9178b355b401d31b0ba78d2e64e79c73ea8a.a2623633.md) per scene, and an asked export ends with `response.smac.export.done`.

details
summary
Show properties
**`aspect`** string optional · Defaults to `16:9`

`16:9`, `9:16` or `1:1`. The motion is saved at this aspect; the assistant cannot save one at any other.

**`export`** object optional

Export the saved motion as a video. Omit it for no video. Video export is not available yet: until it is, the turn ends with `response.smac.export.done` and `status: "unavailable"`, and nothing is rendered or charged.

**`format`** string required

`mp4` or `gif`.

**`size`** string required

`720p`, `1080p` or `2160p`.

**`fps`** integer optional · Defaults to `30`

`30` or `60`. A GIF is always 15 frames a second.

**`captions`** string required

`burned` (drawn into the picture), `file` (a separate file) or `off`.

Example:


```json
{"inputs":[{"role":"user","texts":[{"text":"A 20 s teaser for our launch"}]}],"stream":true,"motion":{"aspect":"9:16","export":{"format":"mp4","size":"1080p","captions":"burned"}}}
```

**`suppress_assistant`** boolean optional · Defaults to `false`

When `true`, the posted user message is persisted and fanned out to everyone present on the thread (via the `message.created` live event) but **no assistant turn runs** — no model call, no response, no SSE stream. Use it for a human-to-human `@user` aside in a shared/group thread: every participant sees the message, but the assistant does not reply. The flag wins even when `stream` is `true`. The endpoint returns a small JSON acknowledgement `{ id, thread_id, created_by_user_id, mention_user_ids, suppressed: true }` instead of a response. When `false` (default), behaviour is unchanged and a normal assistant turn runs.

**`mention_user_ids`** array optional

User IDs this message is addressed to (the `@user` mention targets). Stored on the message and echoed on the `message.created` live event and on the fetched message object (see [List Messages](/api-reference/threads/messages)) so clients can render a "→ to {name}" affordance. An empty or omitted list means no mention (normal addressing). This does not by itself suppress the assistant — pair it with `suppress_assistant: true` for a human-only aside.

**`mention_mode`** string optional · Defaults to `shared-chat`

What the `@`-tags in this message should DO. Send one of the routing mode IDs published by [Get Mention Catalog](/api-reference/mentions/get-mention-catalog) — today `shared-chat`, `conversation` or `communications`. Read the ids from the catalog rather than hardcoding them; a value the catalog does not publish is rejected with a `422`, never silently replaced by the default.

On the message that **creates** the thread this also fixes the thread's assistant participation, once, for the life of the thread:

| `mention_mode` | Who answers afterwards |
|  --- | --- |
| `shared-chat` (default) | The assistant answers every message, and the people you tag can read everything already in the chat. |
| `conversation` | A group chat. The assistant stays out of it — it answers only a message that tags an assistant. An untagged message stays between the people in the thread. |
| `communications` | The tags are delivered as a message on a connected channel. The thread itself behaves like `shared-chat`. |


On an **existing** thread the participation is already settled, so this field only routes the tags for this one send — a later message cannot convert a conversation into a shared AI chat by asserting a mode. In either shape, tagging an assistant (`@[Name](assistant:asst_…)`) makes that assistant answer this turn with the full conversation as its context, and the thread's own assistant stays bound where it was.

Tagging a person **without** tagging an assistant suppresses the assistant turn on its own — no model call, no reply — and the people tagged get a push notification. That rule is derived from the message text, so it holds for every client whether or not it sends this field.

**`new_conversation`** boolean optional · Defaults to `false`

Start a **separate** conversation with the people this message tags, instead of continuing the one that already exists.

The default is what a person expects: a thread is identified by the set of people in it, so tagging someone you already talk to continues that conversation rather than opening another beside it. Send `new_conversation: true` only when the sender explicitly asked for a fresh one — it is the answer to a picker, never a default a client should set on its own.

It applies to both send shapes: the aside that runs no assistant turn, and an ordinary turn that tags people alongside an assistant.

Two refusals, both `409`, and both carry what the client needs to recover in `details`:

| Code | When | What to do |
|  --- | --- | --- |
| `THREAD_DIRECT_ALREADY_EXISTS` | `new_conversation: true` for a one-to-one conversation | A one-to-one conversation is singular by construction — the database permits exactly one per pair per organization. `details.thread_id` names the conversation to open. Do not present this as a failure. |
| `THREAD_PARTICIPANT_CHOICE_REQUIRED` | Several group conversations already exist with these exact people, and the send did not say which | Show `details.candidates` (each with `thread_id`, `title`, `message_count`, `last_activity_at`) as a picker. Re-send the same body with `thread_id` set to the chosen one, or with `details.start_new_with` — `{"new_conversation": true}` — merged in. |


[Resolve Mentions](/api-reference/mentions/resolve-mentions) previews the same decision before the send, so a composer can show "Continuing your conversation with Ana" — or the picker — without sending anything first.

**`output_mode`** string optional · Defaults to `text`

Controls the format of the model's response output. Determines how the response content is structured and returned.

details
summary
Show available modes
- `text` - Rich text with markdown formatting (default). Natural language responses with full markdown support.
- `plain` - Plain text with all markdown formatting stripped. Useful when you need raw text without any formatting.
- `json` - JSON output. The model returns structured JSON. Useful for data extraction and API integrations.
- `structured` - Structured output mode. Returns typed content blocks alongside a `response_blocks` array for programmatic processing.


**`include`** array optional

Specify additional data to include in the response. Each value expands specific parts of the output with extra information. [Learn more](/docs/documentation/core-concepts/response-includes)

details
summary
Show possible types
**Available now:**

- `all` - Include all currently available additional data. Significantly increases response size.
- `web_search.sources` - Include source URLs and metadata from web search results
- `tools.available` - Include list of tools that were available to the model during response generation
- `tools.used` - Include list of tools that were actually used by the model during response generation
- `usage` - Include basic token usage (`input_tokens`, `output_tokens`, `total_tokens`)
- `usage.detailed` - Same token usage as `usage`
- `cost` - Accepted and ignored: no response carries a cost. Spend per day, month, assistant or API key is on `/v1/analytics/usage`
- `rules` - Include list of rules that were applied to this response (rule IDs, names, metadata)
- `rules.debug` - Include detailed rule application metadata (which rules were considered, applied, compressed, or filtered)


**Coming soon:**

- `code_interpreter.outputs` - Python execution outputs and generated files
- `file_search.results` - Matched document chunks and relevance scores
- `message.input_images` - Full image URLs from user messages
- `message.output_images` - Generated image URLs from assistant responses
- `message.logprobs` - Token-level probability scores for generated content
- `reasoning.encrypted` - Encrypted reasoning data for stateless conversations
- `request.logs` - Detailed request processing logs


### Limits and Controls

**`max_output_synapses`** integer optional

Maximum length of the reply, including the model's internal reasoning, from 1 to 2,000,000. The ceiling is the longest reply any model in the catalogue can return. Use it to bound response length.

**`metadata`** object optional

Custom key-value pairs (up to 16) for attaching structured information to the response. Useful for tracking, categorization, or filtering responses in your application. Keys must be ≤64 characters, values ≤512 characters.

**`previous_response_id`** string optional

Reference a previous response to create multi-turn conversations while maintaining context. The model will use the outputs and state from the referenced response as the starting point for this new response. Cannot be used together with `thread`. [Learn more](/docs/documentation/core-concepts/threads#stateless-conversations)

**`vector_store_ids`** array optional

Vector store IDs for file search/RAG. Enables searching uploaded files to answer queries.

**`edit_message_id`** string optional

Message ID to edit. Soft-deletes this message and all subsequent messages, then creates a new branch.

**`previous_message_id`** string optional

Message ID to branch from. Soft-deletes all messages after this message (keeping it), then continues from that point.

**`in_reply_to`** string optional

Message ID (prefixed `msg_`) this message is a structured reply to. Unlike `previous_message_id`, it does **not** branch or truncate the thread — it is pure reply linkage: it is persisted on the new message, echoed back on message fetch (so the reply chip survives a refresh), and the replied-to message is threaded into the model's context. Requires `thread_id`.

**`resume_task_list_id`** string optional

Task list ID to deterministically resume — the id the list was created with (canonically `tasklist_…`, but any id the list actually carries is accepted). When set, the turn reopens the named halted task list's interrupted tasks to pending and instructs the assistant to finish the remaining steps of THAT list — the continuation is not left to the model's discretion. The list must be halted with a non-`cancelled` reason; a cancelled or non-halted list is rejected. Normally set for you by [Resume a halted task list](/api-reference/threads/resume-task-list), which builds the resume request. Requires `thread_id`.

**`client_context`** object optional

The docked assistant panel's view of this turn: which surfaces the user has open, what they have selected, and the current route. Omit it for an ordinary (undocked) turn — its **presence** is what marks a turn as coming from the panel, and it is scoped to the single turn (nothing is stored on the thread).

Requires the **Pinned Copilot** feature to be enabled. If the feature is off, the field is **ignored** and the turn proceeds normally — it is never rejected, so a client that keeps sending it after the feature is switched off does not break.

| Field | Type | Notes |
|  --- | --- | --- |
| `v` | integer | Schema version. Currently `1`. An unrecognised major version is rejected rather than partially parsed. |
| `surfaces` | array | Up to **8** open surfaces, most recent first. Each has `surface` (a stable id, ≤200 chars), `kind`, `title` (≤80 chars), `focus`, `has_selection`, and an optional `version` content hash. Exactly one surface carries `focus`. Extra surfaces are dropped (the focused one is never dropped) and the count is reported to the model. |
| `selection` | object | What the user has highlighted, on one surface: `surface` (**required** when `selection` is sent — omitting it is a 422, not a silent drop), `mode` (`text` or `rows`), `text`, `rows`, and an opaque `range` that is carried through untouched. In `rows` mode each row's `label` (or `title` / `name`) and `id` are shown to the assistant, and `unit` names what one row is as a singular English noun (e.g. `node`, ≤32 characters of letters, spaces and `-`) so the count reads "2 nodes" rather than "2 rows"; a `unit` outside that shape is ignored, never rejected. On a FlowPlate editor route (`route:/flows/flow/<id>`) the selected node ids are also marked as selected in the assistant's view of that automation. Text over **8,000 characters** is shortened to its head and tail with `truncated: true`, and `total_chars` keeps the real length; `rows` is capped at **50**, with `total_rows` keeping the real count. Both totals are corrected to the true size if a client under-reports them, so the assistant is never told that less was cut than really was. |
| `route` | object | The client router's own `name` and `params`. `params` over **1 KB** is dropped while `name` is kept. |
| `dock` | string | Where the panel is rendered (e.g. `right`). Positional information only — it never changes how the assistant answers. |
| `drive_invoked` | boolean | `true` when the user asked the panel to act on the screen through the client's own gesture (a "do it for me" control), rather than by typing a request. Defaults to `false`. |
| `capabilities` | array of strings | Which client-side tools this client can actually run on this turn — any of `read_surface`, `ui_act`, `ui_guide`, `arrange_workspace`. See below; omitting the field and sending `[]` mean different things, and `arrange_workspace` has the opposite default from the other three. |


**Declaring what your client can execute.** Three tools are not run by the API at all — the API asks and *your client* answers: `read_surface` (read an open surface), `ui_act` (act on the screen), and `ui_guide` (walk the user through a flow). A client that is offered one it cannot run leaves the assistant waiting on an answer that never comes, which the user sees as a stall.

`capabilities` is how you avoid that, and the two empty-looking values are not the same answer:

- **Omit the field** — nothing is vetoed, and the turn behaves exactly as it did before this field existed. This is the compatibility value for an older client that predates the field and cannot send it, which is why that client keeps working unchanged. It is **not** the right value for a new surface that cannot run these tools: omitting the field offers all three, and a client that cannot answer them will stall when the assistant reaches for one.
- **`[]`** — an explicit statement that this client can run *none* of them on this turn. All three are withheld from the assistant, which then answers in prose instead of reaching for a tool the panel cannot service. This is the correct value for an inline editor (Cmd+J) or any other new surface that does not host `read_surface` / `ui_act` / `ui_guide`. Sending the key as `null` means the same thing: you sent a declaration and it named nothing. Only leaving the key out entirely is the "I predate this field" case.
- **A partial list** — only the named ones are offered. `["read_surface"]` lets the assistant read the screen while `ui_act` and `ui_guide` stay out of its reach.


**`arrange_workspace` is the other way round: silence is a "no".** It puts the chat on one side of the window and optionally opens a document on the other, and it is offered **only** when `capabilities` contains the exact string `arrange_workspace` on that turn. Omitting the field does not enable it, and neither does sending `[]` — a client that does not name it either cannot rearrange its window or has never heard of the tool, and from the API's side those are the same fact. The three screen tools above have the opposite default only because they predate this field, so a client that never sent it has to keep working; nothing predates `arrange_workspace`. Reading silence as consent would let the assistant record an arrangement no client applies, and then describe a layout the user cannot see.

Declare it when your client can actually move its panes:


```json
{"capabilities": ["read_surface", "ui_act", "ui_guide", "arrange_workspace"]}
```

Names are lower-cased and de-duplicated, and at most **16** are read. Each must look like a tool name — up to **64** characters of lower-case letters, digits, and `_.:-` — and anything else is dropped rather than shortened, since a name that cannot match a tool matches nothing whichever way it is trimmed. A malformed value (a string instead of an array, say) is read as an empty declaration rather than rejected: a turn is never failed over the shape of this field.

**Size limits.** Per-field limits are applied first, then the whole object must serialize to at most **16 KB**; a request whose raw `client_context` exceeds **256 KB** is rejected outright. Anything shortened is reported to the assistant, so it is told what it is *not* seeing rather than shown a fragment as if it were complete.

**Sensitive surfaces.** A surface identified as restricted (for example a credential vault or an encrypted area) is reduced to its bare existence: its title is replaced with a generic label chosen by the API (`Vault`, `Encrypted area`), its version is dropped, and any selection on it is discarded before the turn runs. The assistant is still told the surface is open — it simply cannot see into it. This is applied by the API regardless of how the client classified the surface, so a client that mislabels one does not defeat it.


```json
{
  "v": 1,
  "dock": "right",
  "surfaces": [
    {"surface": "document:doc_ab12", "kind": "document", "title": "Q3 Offsite Plan", "focus": true, "has_selection": true},
    {"surface": "route:settings-departments", "kind": "settings", "title": "Departments"}
  ],
  "selection": {"surface": "document:doc_ab12", "mode": "text", "text": "the two selected paragraphs"},
  "route": {"name": "documents.detail", "params": {"id": "doc_ab12"}},
  "capabilities": ["read_surface"]
}
```

**`resolved_tool_names`** array of strings optional

Tool names from a prior [Resolve tools](/api-reference/copilot/resolve-tools) call. Most tools are not loaded on a turn by default — they sit behind an on-demand discovery step so the assistant is not sent thousands of tokens of tool descriptions it will not use. Naming tools here makes exactly those available immediately, so a turn that needs one does not spend a round trip discovering it.

Omit it and the assistant discovers tools the way it always has. That fallback is always correct, and it is what the resolve endpoint asks for when it answers `"resolver": "fallback"` — a fallback needs no retry and is not an error.

Naming a tool never grants access to it. The names are matched against the tools this caller can already use; anything else is ignored, including a name that no longer exists. Up to **16** names are used per request; more than that is a sign the names did not come from the resolve endpoint, and the extras are dropped. Names carry over to later turns in the same thread and stop being carried after a few turns without use, so a long conversation does not accumulate them.

Requires the **Pinned Copilot** feature to be enabled. If the feature is off, the field is **ignored** and the turn proceeds normally — it is never rejected.


```json
{
  "resolved_tool_names": ["orgadmin_update_user_role", "list_departments"]
}
```

**`workflow_id`** string optional

Workflow/Flow ID from FlowPlate. Used to associate threads with specific workflow automations.

**`mode`** string optional

Agent mode for the **Smith flow agent**. Selects how Smith operates on a flow for this request. **Only applies when `assistant_id` targets the Smith assistant** — it is ignored for every other assistant. Pair it with `metadata.flow_id` (the flow Smith should read and edit). Defaults to `build` on the Smith surface when omitted.

details
summary
Show available modes
- `build` - Create and modify the flow graph. Smith adds, edits, and wires nodes, authors custom nodes, persists changes, and can run the flow server-side. Requires edit permission on the flow.
- `plan` - Propose changes without mutating the graph. Smith describes what it would do (read-only); nothing is persisted. Requires view permission on the flow.
- `chat` - Converse about the flow without mutating the graph. Read-only, view permission.
- `fix` - Debug the flow — inspect, diagnose, and correct errors in the graph. Requires edit permission on the flow.


An unknown value returns HTTP 422. See the [Smith (flow agent)](/docs/api-reference/responses/smith) reference for the full flow surface, including the `response.flow.*` streaming blocks Smith emits.

### Skills

Skills are reusable, named instruction blocks (summarize, translate, review-code, etc.). All skill execution happens through this endpoint — there is no separate "execute skill" endpoint. There are two ways a skill can run during a response:

1. **Catalog + autonomous invocation (default).** The server injects a compact skill catalog (one line per skill) into the system prompt. The model sees the catalog and can call the `invoke_skill` system tool when the conversation matches a skill's purpose. The tool resolves the skill and returns its instructions as a tool result.
2. **Forced server-side injection.** Set `force_skill_id` to make the server resolve and inject a specific skill's content as a system message for this turn. Strict: invalid IDs return HTTP 422. Use this when the client knows exactly which skill should run (e.g. a user clicked a "Use this skill" button or typed a slash command in your UI).


Skills with `disable_model_invocation: true` are hidden from path 1 (the catalog) — they only run via path 2 (explicit `force_skill_id`).

**`skill_ids`** array optional

Explicit list of skill IDs to scope the catalog for this request. When set, **only** these skills appear in the catalog and are valid targets for `invoke_skill`. Accepts personal or org skill IDs (`skill_*`) and platform skill names (`platform-*`). Unknown or inaccessible IDs are silently skipped (permissive). An empty list `[]` opts out of skill injection entirely. Omit (`null`) to fall back to the assistant's attached skills, then the user's full library.

**`force_skill_id`** string optional

When set, the server resolves this skill (with empty arguments) and injects its content as a system message for this turn only, forcing the model to follow the skill's instructions. Accepts a personal or org skill ID (`skill_*`) or a platform skill name (`platform-*`). **Strict**: an invalid or inaccessible ID returns HTTP 422. Works alongside `skill_ids` — you can force one skill AND scope the catalog to a subset.

### Reasoning

**`reasoning`** object optional

**Unified reasoning configuration** that works across all reasoning-capable models (OpenAI GPT-5/O-series, Anthropic Claude). Controls how much computational effort the model dedicates to internal chain-of-thought processing before generating the final output. The API automatically maps your effort level to provider-specific parameters. [Learn more](/docs/documentation/reasoning/reasoning-models)

details
summary
Show properties
**`effort`** string optional · Defaults to `medium`

Controls the computational effort spent on reasoning/thinking. Higher effort produces more thorough analysis but increases response time and token usage.

**Available values:** `off`, `low`, `medium`, `high`, `maximum`

[View provider mapping details →](/docs/documentation/reasoning/reasoning-models#provider-mapping)

**`summary`** string optional

Request a summary of the model's reasoning process.

**Available values:**

- `off` (disable reasoning summary)
- `auto` (model decides)
- `concise` (brief overview)
- `detailed` (comprehensive explanation)


**Note:** Some models do not stream reasoning content in real-time, only metadata events (`response.reasoning.started`, `response.reasoning.completed`). Other models stream full thinking content as it's generated via `response.reasoning.delta`.

### Tools

**`tools`** array optional

Array of custom tools the model can invoke during response generation. Supports custom function calls, personal connectors, and MCP server tools. Tool usage can be controlled via the `tool_choice` parameter. For built-in capabilities like web search, file search, and code execution, use the `system_tools` parameter instead. [Learn more](/docs/documentation/core-concepts/function-calling)

details
summary
Show possible types
details
summary
Personal Connectors
Access to external services and APIs through configured personal connectors. Enables the model to interact with services like Gmail, Google Calendar, Dropbox, and more. [Learn more about personal connectors →](/assets/list.6d1173e7d4e45479a60b413a26b8f0caeb5d4203780db8da947e28edce5cc602.922461b3.md)

Personal Connectors object

details
summary
Show properties
**`type`** string required

The type of tool. Always set to `personalConnector`.

**`configuration_ids`** array of strings optional

Specific personal connector configuration IDs to use. If not provided, all enabled configurations for the authenticated user or API key will be available. Each string should be a valid configuration ID (e.g., `pconf_abc123`).

details
summary
Custom MCP
Connect to Model Context Protocol (MCP) servers to access additional tools and capabilities. MCP enables Claude to interact with external services, databases, APIs, and custom integrations through standardized protocols. [Learn more](/docs/documentation/personal-connectors/mcp)

MCP Tool object

details
summary
Show properties
**`type`** string required

The type of tool. Always set to `mcp`.

**`server_label`** string required

A unique label identifying this MCP server connection. Used to distinguish between multiple MCP servers in a single request and appears in tool calls and logs. Example: `"database_server"`, `"api_integration"`.

**Server Connection**

Choose one of the following methods to connect to an MCP server:

**`configuration_id`** string optional

ID of a saved MCP configuration (`mcp_…`) — reference a pre-configured connector that has been set up in advance. [See MCP Configurations API](/assets/create.d58ca3f0dc4a646d4e65ee06a1c339328f0dda095b47fb04fbab7c9c7fab3bda.53b2743b.md) — **or** the id of an attached personal-connector credential (`tcred_…`), which the server resolves to that connector's tools for the current user. Passing a `tcred_…` id inline lets you run a connector-backed tool without first saving an assistant (for example, when testing an unsaved assistant configuration). Also accepted under the alias `connector_id`.

**`server_url`** string optional

Direct URL to a custom MCP server endpoint. Use this for connecting to your own MCP-compatible servers or third-party MCP services. Example: `"https://mcp.example.com/v1"`.

**Note:** One of `server_url` or `configuration_id` must be provided (but not both).

**Authentication**

**`authorization`** string optional

OAuth access token or API key for authenticating with the remote MCP server. Required when using custom MCP servers via `server_url` or when using service connectors that require additional authentication. Your application handles the OAuth flow and provides the token here.

**`headers`** object optional

Custom HTTP headers to send with requests to the MCP server. Used for authentication, API versioning, or other server-specific requirements. Example: `{"X-API-Key": "your-key", "Authorization": "Bearer token"}`.

**Tool Filtering**

**`allowed_tools`** array or object optional

Restrict which tools from the MCP server the model can invoke. By default, all available tools are accessible. Use this to limit access for security or to focus the model on specific capabilities.

details
summary
Show examples
**Array format** - List specific tool names:


```json
["search_documents", "list_files", "get_file_content"]
```

**Filter object** - Use patterns to include/exclude tools:


```json
{
  "include": ["search_*", "get_*"],
  "exclude": ["delete_*", "update_*"]
}
```

details
summary
Custom Function Calls
Custom functions defined by you that the model can call with strongly-typed arguments. Enables the model to interact with your application code, APIs, or external services. [Learn more](/docs/documentation/core-concepts/function-calling)

Function object

details
summary
Show properties
**`type`** string required

The type of tool. Always set to `function`.

**`name`** string required

Unique identifier for the function. Used by the model to reference and invoke the function. Must be alphanumeric with underscores (e.g., `get_weather`, `calculate_total`).

**`description`** string optional

Human-readable explanation of what the function does. The model uses this to determine when and how to call the function. Be specific and clear.

**`parameters`** object required

JSON Schema object defining the function's input parameters. Specifies parameter names, types, descriptions, and whether they're required.

**`strict`** boolean optional · Defaults to `true`

Enforce strict parameter validation. When `true`, the model guarantees parameters match the schema exactly. When `false`, allows best-effort parameter generation.

### System Tools

**`system_tools`** object optional

Configuration for built-in system tools provided by Aitronos. These extend model capabilities beyond the custom tools in the `tools` array. System tools are enabled/disabled via mode settings rather than being defined inline.

details
summary
Show available system tools
**`image_operations`** object optional

Enable AI image generation and editing capabilities. When enabled, the model can generate and edit images; the underlying image engine is selected automatically by Aitronos. [Learn more](/docs/documentation/system-tools/image-generation)

details
summary
Show properties
**`mode`** string required

Controls when image operations are available. Values: `on` (always enable), `off` (disable), `auto` (model decides based on context).

When enabled, the model can call the `generate_image` tool with these parameters:

| Parameter | Type | Default | Description |
|  --- | --- | --- | --- |
| `prompt` | string | required | Text description of the desired image |
| `n` | integer | `1` | Number of images (1-10) |
| `size` | string | `1024x1024` | Image dimensions: `1024x1024`, `1024x1536`, `1536x1024`, `auto` |
| `quality` | string | `hd` | Quality: `standard`, `hd`, `low`, `medium`, `high`, `auto` |
| `style` | string | `vivid` | Style: `vivid`, `natural` |
| `output_format` | string | `png` | Format: `png`, `webp`, `jpeg` |
| `background` | string | `auto` | Background: `transparent`, `opaque`, `auto` |
| `input_image` | string | - | URL or base64 for editing mode |
| `input_mask` | string | - | Mask for inpainting |
| `input_fidelity` | string | `low` | Fidelity: `low`, `high` |
| `user` | string | - | User ID for tracking |


The underlying image model and engine are selected automatically by Aitronos based on the requested operation (generation vs. editing), image quality, and your organization's available capabilities — you do not pick a raw provider or model. Quality and format hints above are honored where the selected engine supports them.

**`web_search`** object optional

Enable web search capabilities.

details
summary
Show properties
**`mode`** string required

Values: `on`, `off`, `auto`.

**`deep_search`** object optional

Opt in to reading full page content from web search results (instead of only snippets). Slower and more expensive — off by default. When enabled, the model is allowed to request `fetch_content=true` per call and the backend fetches the top N result URLs using a scraping engine that also handles JavaScript-rendered pages.

details
summary
Show properties
**`enabled`** boolean · Defaults to `false`

Master opt-in. When `false`, `web_search` returns snippet-only results (current behavior). When `true`, the model sees extra `fetch_content` / `max_pages` parameters on the `web_search` tool and decides per call whether to fetch.

**`max_pages`** integer · Defaults to `3` · Range `1`–`5`

Maximum number of result URLs to fetch per `web_search` call. The LLM-supplied `max_pages` argument is always clamped to this value.

**`render_js`** string · Defaults to `auto`

JS-rendering policy: `auto` (fast static fetch first, browser fallback for JS-heavy pages), `always` (always use the browser engine), or `never` (static-only).

**`per_page_timeout_ms`** integer · Defaults to `8000` · Range `1000`–`30000`

Hard cap per URL fetch in milliseconds.

**`max_chars_per_page`** integer · Defaults to `6000` · Range `500`–`50000`

Each fetched page is truncated to this many characters before being injected into the LLM context.

**`code_interpreter`** object optional

Enable code execution capabilities.

details
summary
Show properties
**`mode`** string required

Values: `on`, `off`, `auto`.

**`file_search`** object optional

Enable file search capabilities.

details
summary
Show properties
**`mode`** string required

Values: `on`, `off`, `auto`.

**Example:**


```json
{
  "inputs": [{"role": "user", "content": "Generate an image of a sunset over mountains"}],
  "model": "ftg-3.0",
  "system_tools": {
    "image_operations": {"mode": "on"}
  }
}
```

### Output Format

### Response Parameters

**`temperature`** number optional

Controls output randomness and creativity. Values range from `0.0` to `1.0`. Lower values (e.g., `0.2`) produce focused, deterministic outputs ideal for factual tasks. Higher values (e.g., `0.8`) generate more varied, creative responses suited for brainstorming. Adjust either `temperature` or `top_p`, not both.

**`top_p`** number optional · Defaults to `1.0`

Alternative to `temperature` for controlling randomness via nucleus sampling. Only the top cumulative probability mass up to `top_p` is considered. For example, `0.9` means only the top 90% probability mass is used. Values range from `0.0` to `1.0`. Adjust either `top_p` or `temperature`, not both.

## Body parameters

**`assistant_name`** string optional

The assistant name.

**`frequency_penalty`** number optional

The frequency penalty.

**`deep_search_config`** object optional

The deep search config.

**`partial_context`** object optional

Partial state from an interrupted stream, sent by the frontend to enable recovery.

**`parallel_tool_calls`** boolean optional

The parallel tool calls.

**`service_tier`** string optional

The provider serving path: `auto`, `default`, `flex` (cheaper, deferred), or
`priority` (premium, faster). `priority` takes precedence over `speed_tier`; the
neutral values (`auto`, `default`) do not — they leave `speed_tier` to select the
serving path, so an assistant whose stored `service_tier` is `auto` still honours
the tier you ask for. Requesting `priority` requires the `use_speed_mode`
capability — without it the request is rejected `403 INSUFFICIENT_PERMISSIONS`.

**`speed_tier`** string optional

Per-model speed tier id, from `GET /v1/models/speed-options` (for example
`standard` or `fast`). Selects the serving path when `service_tier` is not set,
and changes the billed rate. A tier the model does not support falls back to the
model's default. Requesting a **non-default** tier requires the `use_speed_mode`
capability — without it the request is rejected `403 INSUFFICIENT_PERMISSIONS`.
Members without the capability may always run the model's default tier, and
`speed-options` reports `speed_supported: false` for them so the control can be
hidden.

**`thread_context_mode`** string optional

The thread context mode.

**`assistant_instructions`** string optional

The assistant instructions.

**`assistant_description`** string optional

The assistant description.

**`max_completion_tokens`** integer optional

Maximum output length in tokens, from 1 to 2,000,000. When `max_output_synapses` is also sent, `max_completion_tokens` is the one applied.

**`slice_ids`** array[string] optional

The slice ids.

**`prefer_deep_search`** boolean optional

The prefer deep search.

**`max_tool_calls`** integer optional

The max tool calls.

**`is_incognito`** boolean optional

Enable incognito mode. Thread will not be saved, personal memories will not be extracted or used. Ignored if thread_id references an existing thread.

**`document_tools_mode`** string optional

When `"read_only"`, only document-surface read tools (`search_documents`, `smoc_fetch_item`, `smoc_get_export_status`) are advertised and document writers are denied at execution. Use this for an inline editor turn whose JSON envelope the client applies. Omit for the default read+write surface. Returns `422 DOCUMENT_TOOLS_MODE_UNAVAILABLE` if the document tool surface is off for the caller. If that setting could not be checked for this request, the response is `503 SERVICE_TEMPORARILY_UNAVAILABLE` with `details.reason` set to `document_tools_flag_unreadable` and a `Retry-After` header: retry the same request after that delay. A `500 INTERNAL_ERROR` with the same `details.reason` means the check failed in a way a retry will not fix. Other values are rejected.

An inline editor (Cmd+J) turn that also sends document identity must send the gate **and** a focused document surface together. `selection.surface` is required when `selection` is present. That surface does not host client-executed tools, so send `capabilities: []` — omitting `capabilities` offers `read_surface` / `ui_act` / `ui_guide` and the turn stalls when the model reaches for them.


```json
{
  "document_tools_mode": "read_only",
  "store": false,
  "is_incognito": true,
  "client_context": {
    "v": 1,
    "dock": null,
    "surfaces": [
      {
        "surface": "smoc:itm_<32 lowercase hex>",
        "kind": "document",
        "title": "<doc title>",
        "focus": true,
        "has_selection": true
      }
    ],
    "selection": {
      "surface": "smoc:itm_<32 lowercase hex>",
      "mode": "text",
      "implicit": true,
      "truncated": false,
      "text": "<up to 8000 chars, 60/25 head/tail>"
    },
    "capabilities": []
  }
}
```

**`presence_penalty`** number optional

The presence penalty.

## Returns

**Non-streaming (`stream: false`):** Returns a complete [Response object](/docs/api-reference/objects/response-object) after generation finishes.

**Streaming (`stream: true`):** Returns [Streaming event objects](/docs/api-reference/objects/streaming-event-object) as Server-Sent Events (SSE). Each event contains a specific event type and data payload showing the model's progress, tool calls, and generated content in real-time.

**Busy thread (`202 Accepted`):** A thread runs **one response at a time**. When a send targets a thread that already has a response in progress (or earlier sends waiting), the message is placed into the thread's server-side send queue instead of starting a parallel response, and the request returns `202` with a JSON body — even when `stream: true` was requested:


```json
{
  "success": true,
  "queued": true,
  "thread_id": "thread_abc123",
  "queue_item": {
    "id": "qitem_9f2c…",
    "client_id": "auto_req_…",
    "text": "the queued message",
    "status": "queued",
    "server_seq": 2,
    "run_id": null
  },
  "active_run_id": "run_7d1e…"
}
```

Queued messages run strictly in arrival order, one at a time, as soon as the active response completes — including when the sender disconnects. Track progress via the thread's live stream (`queue.item_updated`, `message.created`) or `GET /v1/threads/{thread_id}/queue`.

Basic Request

```bash
curl https://api.aitronos.com/v1/model/response \
  -H "X-API-Key: $FREDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_id": "org_abc123",
    "model": "ftg-3.0",
    "inputs": [
      {
        "role": "user",
        "content": "What is the capital of France?"
      }
    ]
  }'
```


```python Python SDK
from aitronos import Aitronos

client = Aitronos(api_key="your-api-key")

response = client.responses.create_response(
    organization_id="org_...",
    inputs=[{"role": "user", "content": [{"type": "input_text", "text": "Hello!"}]}],
)
print(response)
```


```python
import os
import requests

api_key = os.environ["FREDDY_API_KEY"]

response = requests.post(
    "https://api.aitronos.com/v1/model/response",
    headers={"X-API-Key": api_key, "Content-Type": "application/json"},
    json={
        "organization_id": "org_abc123",
        "model": "ftg-3.0",
        "inputs": [{"role": "user", "content": "What is the capital of France?"}],
    },
)

result = response.json()
print(result["response"][0]["text"])
```


```javascript
const apiKey = process.env.FREDDY_API_KEY;

const response = await fetch('https://api.aitronos.com/v1/model/response', {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    organization_id: 'org_abc123',
    model: 'ftg-3.0',
    inputs: [
      {
        role: 'user',
        texts: [
          {
            text: 'What is the capital of France?'
          }
        ]
      }
    ]
  })
});

const result = await response.json();
console.log(result.response[0].text);
```

With Assistant

```bash
curl https://api.aitronos.com/v1/model/response \
  -H "X-API-Key: $FREDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_id": "org_abc123",
    "assistant_id": "asst_abc123",
    "inputs": [
      {
        "role": "user",
        "content": "Analyze this data and provide insights"
      }
    ]
  }'
```


```python Python SDK
from aitronos import Aitronos

client = Aitronos(api_key="your-api-key")

response = client.responses.create_response(
    organization_id="org_...",
    inputs=[{"role": "user", "content": [{"type": "input_text", "text": "Hello!"}]}],
)
print(response)
```


```python
import requests

api_key = os.environ["FREDDY_API_KEY"]

response = requests.post(
    "https://api.aitronos.com/v1/model/response",
    headers={"X-API-Key": api_key, "Content-Type": "application/json"},
    json={
        "organization_id": "org_abc123",
        "assistant_id": "asst_abc123",
        "inputs": [
            {"role": "user", "content": "Analyze this data and provide insights"}
        ],
    },
)

result = response.json()
print(result["response"][0]["text"])
```


```javascript
const apiKey = process.env.FREDDY_API_KEY;

const response = await fetch('https://api.aitronos.com/v1/model/response', {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    organization_id: 'org_abc123',
    assistant_id: 'asst_abc123',
    inputs: [
      {
        role: 'user',
        texts: [
          {
            text: 'Analyze this data and provide insights'
          }
        ]
      }
    ]
  })
});

const result = await response.json();
console.log(result.response[0].text);
```

With Tools

```bash
curl https://api.aitronos.com/v1/model/response \
  -H "X-API-Key: $FREDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_id": "org_abc123",
    "model": "ftg-3.0",
    "inputs": [
      {
        "role": "user",
        "content": "Search the web for the latest AI news"
      }
    ],
    "tools": [
      {
        "type": "webSearch",
        "search_context_size": "medium"
      }
    ]
  }'
```


```python Python SDK
from aitronos import Aitronos

client = Aitronos(api_key="your-api-key")

response = client.responses.create_response(
    organization_id="org_...",
    inputs=[{"role": "user", "content": [{"type": "input_text", "text": "Hello!"}]}],
)
print(response)
```


```python
import requests

api_key = os.environ["FREDDY_API_KEY"]

response = requests.post(
    "https://api.aitronos.com/v1/model/response",
    headers={"X-API-Key": api_key, "Content-Type": "application/json"},
    json={
        "organization_id": "org_abc123",
        "model": "ftg-3.0",
        "inputs": [
            {"role": "user", "content": "Search the web for the latest AI news"}
        ],
        "tools": [{"type": "webSearch", "search_context_size": "medium"}],
    },
)

result = response.json()
print(result["response"][0]["text"])
```


```javascript
const apiKey = process.env.FREDDY_API_KEY;

const response = await fetch('https://api.aitronos.com/v1/model/response', {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    organization_id: 'org_abc123',
    model: 'ftg-3.0',
    inputs: [
      {
        role: 'user',
        texts: [
          {
            text: 'Search the web for the latest AI news'
          }
        ]
      }
    ],
    tools: [
      {
        type: 'webSearch',
        search_context_size: 'medium'
      }
    ]
  })
});

const result = await response.json();
console.log(result.response[0].text);
```

Streaming

```bash
curl https://api.aitronos.com/v1/model/response \
  -H "X-API-Key: $FREDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_id": "org_abc123",
    "model": "ftg-3.0",
    "stream": true,
    "inputs": [
      {
        "role": "user",
        "content": "Write a short story about AI"
      }
    ]
  }'
```


```python Python SDK
from aitronos import Aitronos

client = Aitronos(api_key="your-api-key")

response = client.responses.create_response(
    organization_id="org_...",
    inputs=[{"role": "user", "content": [{"type": "input_text", "text": "Hello!"}]}],
)
print(response)
```


```python
import requests

api_key = os.environ["FREDDY_API_KEY"]

response = requests.post(
    "https://api.aitronos.com/v1/model/response",
    headers={"X-API-Key": api_key, "Content-Type": "application/json"},
    json={
        "organization_id": "org_abc123",
        "model": "ftg-3.0",
        "stream": True,
        "inputs": [{"role": "user", "content": "Write a short story about AI"}],
    },
    stream=True,
)

for line in response.iter_lines():
    if line:
        print(line.decode("utf-8"))
```


```javascript
const apiKey = process.env.FREDDY_API_KEY;

const response = await fetch('https://api.aitronos.com/v1/model/response', {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    organization_id: 'org_abc123',
    model: 'ftg-3.0',
    stream: true,
    inputs: [
      {
        role: 'user',
        texts: [
          {
            text: 'Write a short story about AI'
          }
        ]
      }
    ]
  })
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log(decoder.decode(value));
}
```

JSON Mode

```bash
curl https://api.aitronos.com/v1/model/response \
  -H "X-API-Key: $FREDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_id": "org_abc123",
    "model": "ftg-3.0",
    "output_mode": "json",
    "inputs": [
      {
        "role": "user",
        "content": "List three popular programming languages with their use cases in JSON format"
      }
    ]
  }'
```


```python Python SDK
from aitronos import Aitronos

client = Aitronos(api_key="your-api-key")

response = client.responses.create_response(
    organization_id="org_...",
    inputs=[{"role": "user", "content": [{"type": "input_text", "text": "Hello!"}]}],
)
print(response)
```


```python
import requests

api_key = os.environ["FREDDY_API_KEY"]

response = requests.post(
    "https://api.aitronos.com/v1/model/response",
    headers={"X-API-Key": api_key, "Content-Type": "application/json"},
    json={
        "organization_id": "org_abc123",
        "model": "ftg-3.0",
        "output_mode": "json",
        "inputs": [
            {
                "role": "user",
                "content": "List three popular programming languages with their use cases in JSON format",
            }
        ],
    },
)

result = response.json()
print(result["response"][0]["text"])
```


```javascript
const apiKey = process.env.FREDDY_API_KEY;

const response = await fetch('https://api.aitronos.com/v1/model/response', {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    organization_id: 'org_abc123',
    model: 'ftg-3.0',
    output_mode: 'json',
    inputs: [
      {
        role: 'user',
        texts: [
          {
            text: 'List three popular programming languages with their use cases in JSON format'
          }
        ]
      }
    ]
  })
});

const result = await response.json();
console.log(result.response[0].text);
```

JSON Schema Output

```bash
curl https://api.aitronos.com/v1/model/response \
  -H "X-API-Key: $FREDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_id": "org_abc123",
    "model": "ftg-3.0",
    "output_mode": "json_schema",
    "json_schema": {
      "id": "weather_forecast",
      "description": "Weather forecast data",
      "schema": {
        "type": "object",
        "properties": {
          "temperature": {"type": "number"},
          "conditions": {"type": "string"},
          "location": {"type": "string"}
        },
        "required": ["temperature", "conditions", "location"]
      }
    },
    "inputs": [
      {
        "role": "user",
        "content": "What is the weather in Paris?"
      }
    ]
  }'
```


```python Python SDK
from aitronos import Aitronos

client = Aitronos(api_key="your-api-key")

response = client.responses.create_response(
    organization_id="org_...",
    inputs=[{"role": "user", "content": [{"type": "input_text", "text": "Hello!"}]}],
)
print(response)
```


```python
import requests

api_key = os.environ["FREDDY_API_KEY"]

response = requests.post(
    "https://api.aitronos.com/v1/model/response",
    headers={"X-API-Key": api_key, "Content-Type": "application/json"},
    json={
        "organization_id": "org_abc123",
        "model": "ftg-3.0",
        "output_mode": "json_schema",
        "json_schema": {
            "id": "weather_forecast",
            "description": "Weather forecast data",
            "schema": {
                "type": "object",
                "properties": {
                    "temperature": {"type": "number"},
                    "conditions": {"type": "string"},
                    "location": {"type": "string"},
                },
                "required": ["temperature", "conditions", "location"],
            },
        },
        "inputs": [{"role": "user", "content": "What is the weather in Paris?"}],
    },
)

result = response.json()
print(result)
```


```javascript
const apiKey = process.env.FREDDY_API_KEY;

const response = await fetch('https://api.aitronos.com/v1/model/response', {
  method: 'POST',
  headers: {
    'X-API-Key': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    organization_id: 'org_abc123',
    model: 'ftg-3.0',
    output_mode: 'json_schema',
    json_schema: {
      id: 'weather_forecast',
      description: 'Weather forecast data',
      schema: {
        type: 'object',
        properties: {
          temperature: { type: 'number' },
          conditions: { type: 'string' },
          location: { type: 'string' }
        },
        required: ['temperature', 'conditions', 'location']
      }
    },
    inputs: [
      {
        role: 'user',
        texts: [
          {
            text: 'What is the weather in Paris?'
          }
        ]
      }
    ]
  })
});

const result = await response.json();
console.log(result);
```

**Response:**

Non-Streaming
Complete Response object returned after generation finishes:


```json
{
  "success": true,
  "thread_id": "thrd_xyz789",
  "response": [
    {
      "type": "text",
      "text": "The capital of France is Paris. It is located in the north-central part of the country and is known for its art, culture, and iconic landmarks like the Eiffel Tower."
    }
  ],
  "response_id": "resp_abc123",
  "is_summarized": false,
  "stop_reason": "end_turn"
}
```

Streaming Events
Server-Sent Events (SSE) streamed incrementally:


```
data: {"event":"response.created","status":"in_progress","thread_id":"thrd_xyz789","is_summarized":false,"output_mode":"text"}

data: {"event":"response.content_delta","delta":"The capital of France"}

data: {"event":"response.content_delta","delta":" is Paris."}

data: {"event":"response.content_delta","delta":" It is located in the north-central part of the country."}

data: {"event":"response.completed","status":"completed","thread_id":"thrd_xyz789","response_id":"resp_abc123","is_summarized":false,"stop_reason":"end_turn","response":[{"type":"text","text":"The capital of France is Paris. It is located in the north-central part of the country."}]}
```

Streaming with Tools
Events showing tool execution flow:


```
data: {"event":"response.created","status":"in_progress","thread_id":"thrd_456","is_summarized":false,"output_mode":"structured"}

data: {"event":"response.content_delta","delta":"I'll check the current weather in Paris.\n\n"}

data: {"event":"response.block","block":{"type":"tool_call","tool_name":"get_weather","arguments":{"city":"Paris"},"success":true,"execution_time_ms":245,"id":"call_paris_weather","result":"{\"temperature\":15,\"condition\":\"Partly Cloudy\"}"}}

data: {"event":"response.content_delta","delta":"## Result\nThe current weather in Paris is 15°C and partly cloudy."}

data: {"event":"response.completed","status":"completed","thread_id":"thrd_456","response_id":"resp_x9y8z7","is_summarized":false,"response_blocks":[{"type":"text","content":"I'll check the current weather in Paris."},{"type":"tool_call","tool_name":"get_weather","arguments":{"city":"Paris"},"success":true,"execution_time_ms":245,"id":"call_paris_weather","result":"{\"temperature\":15,\"condition\":\"Partly Cloudy\"}"},{"type":"text","content":"## Result\nThe current weather in Paris is 15°C and partly cloudy."}]}
```

JSON Mode Response
Response with JSON output — the model's JSON is serialized as a string inside the `text` field:


```json
{
  "success": true,
  "thread_id": "thrd_xyz789",
  "response": [
    {
      "type": "text",
      "text": "{\"languages\": [{\"name\": \"Python\", \"use_case\": \"Data science, web development, automation\"}, {\"name\": \"JavaScript\", \"use_case\": \"Web development, frontend and backend\"}, {\"name\": \"Java\", \"use_case\": \"Enterprise applications, Android development\"}]}"
    }
  ],
  "response_id": "resp_json123",
  "is_summarized": false,
  "stop_reason": "end_turn"
}
```

Parse the JSON string from `response[0]["text"]` to get the structured object.

400 Bad Request

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The provided value is invalid.",
    "system_message": "Missing required field: organization_id",
    "type": "client_error",
    "status": 400,
    "details": {
      "field": "organization_id"
    },
    "trace_id": "abc-123-def",
    "timestamp": "2025-12-22T15:30:00Z"
  }
}
```

401 Unauthorized

```json
{
  "success": false,
  "error": {
    "code": "AUTHENTICATION_REQUIRED",
    "message": "Authentication required. Please provide a valid API key.",
    "system_message": "Missing or invalid authorization header",
    "type": "authentication_error",
    "status": 401,
    "details": {},
    "trace_id": "abc-123-def",
    "timestamp": "2025-12-22T15:30:00Z"
  }
}
```

429 Rate Limit

```json
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "You've made too many requests. Please slow down and try again.",
    "system_message": "Rate limit exceeded",
    "type": "client_error",
    "status": 429,
    "details": {
      "retry_after": 60
    },
    "trace_id": "abc-123-def",
    "timestamp": "2025-12-22T15:30:00Z"
  }
}
```

500 Server Error

```json
{
  "success": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Something went wrong on our end. Please try again later.",
    "system_message": "An internal server error occurred",
    "type": "server_error",
    "status": 500,
    "details": {},
    "trace_id": "abc-123-def",
    "timestamp": "2025-12-22T15:30:00Z"
  }
}
```

## Working with Files and Images

The Freddy API supports attaching files and images to conversations. Upload files first, then reference them by `file_id`.

### File References (All Models)

Upload files via the [Files API](/docs/api-reference/files/upload), then attach them to messages using the `files` array. The system automatically handles each file type:

- **Documents** (PDF, DOCX, XLSX, TXT, MD, etc.): Text is extracted and relevant chunks are injected into the conversation context.
- **Images** (PNG, JPEG, WebP): Sent as visual input to vision-capable models (GPT-4o, Claude, etc.). The model can see and describe the image.
- **Code files** (PY, JS, TS, HTML, CSS, etc.): Content is extracted and provided as context.


**Workflow:**

1. Upload file via [Files API](/docs/api-reference/files/upload) → receive `file_id`
2. Reference `file_id` in the `files` array on the input message
3. Model receives the content as context (text) or visual input (images)



```json
{
  "inputs": [{
    "role": "user",
    "content": "What do you see in this image?",
    "files": [{ "file_id": "file_abc123def456abc123def456abc12345" }]
  }]
}
```

**Limits:**

- Max 10 files per message
- Max file size: 100MB (direct upload), unlimited (resumable upload)


[View file upload documentation →](/docs/api-reference/files/upload)

### Vision API for Structured Data

Extract structured data from images with JSON schema validation using a dedicated endpoint.

**Use cases:**

- Invoice processing
- Receipt scanning
- ID card extraction
- Form data capture


[View Vision API documentation →](/assets/analyze-image.522ab94b7389f3cd6f5f246da0b5e8256c8002501c3d5a84c27d097c1cb77b29.724c4ffc.md)

## Related Resources

- [List style profiles](/docs/api-reference/style-profiles/list) — `kind=note` catalog for `style_notes`
- [Smith (flow agent)](/docs/api-reference/responses/smith)
- [Resolve copilot tools](/docs/api-reference/copilot/resolve-tools) — pre-resolve `resolved_tool_names` for a docked turn
- [List flow checkpoints](/assets/list-flow-checkpoints.debb42e121d3941dc61bb6897f6a29c665eda9b29bb27c6d7718c6395dda8b05.f3388c00.md)
- [Upload Files](/docs/api-reference/files/upload)
- [Vision API - Analyze Image](/assets/analyze-image.522ab94b7389f3cd6f5f246da0b5e8256c8002501c3d5a84c27d097c1cb77b29.724c4ffc.md)
- [Function Calling Guide](/docs/documentation/core-concepts/function-calling)
- [Structured Output Guide](/docs/documentation/core-concepts/structured-output)
- [Text Generation Guide](/docs/documentation/core-concepts/text-generation)
- [Authentication Guide](/docs/api-reference/authentication)