# Upstage Studio

> Upstage Studio turns documents into structured data with workflows (parse, classify, extract, validate, merge, export), stores results in collections, and lets coworkers (chat agents) work with that data and with connected tools. This file tells AI assistants what they can do with an API key, what still needs the web UI, and how to operate the web UI without mistakes.

## What you can do today

| Task | How |
|---|---|
| Run an existing workflow on a document | API. See "Run a workflow" |
| Chat with an existing coworker | API. See "Chat with a coworker" |
| Search a collection | API. See "Search a collection" |
| Create or edit a workflow, schema, coworker, or collection | Web UI only. There is no public API for building yet. See "Operating the web UI" |

Do not call paths under `https://studio.upstage.ai/api/`. They are internal, need a browser session, and change without notice. The public API lives at `https://api.upstage.ai/v2`.

## Authentication

```
Authorization: Bearer $UPSTAGE_API_KEY
```

Read the key from the `UPSTAGE_API_KEY` environment variable. Never write a literal key into code, config files, or the conversation, and do not ask the user to paste one into the chat. Keys are issued at https://console.upstage.ai/api-keys.

A resource that belongs to a Project needs the `x-project-id` header on every request. The Code dialog of that resource in Studio shows the header with the right value.

## Run a workflow

Each workflow publishes its own instructions, with its ID filled in, at:

```
https://studio.upstage.ai/agents/{workflow_id}/code.md
```

Fetch that file before writing code. The short version: upload a file with `POST https://api.upstage.ai/v2/files`, start a job with `POST https://api.upstage.ai/v2/responses` (`model` is the workflow ID), then poll `GET https://api.upstage.ai/v2/responses/{job_id}` until `status` is `completed`, `failed`, or `cancelled`.

A failed job carries `error: { code, step, message }`. `step` is the stage that failed and `message` is the reason. Report both instead of guessing. Stop a job you started by mistake with `POST https://api.upstage.ai/v2/responses/{job_id}/cancel`.

## Chat with a coworker

Coworkers do not run through `/v2/responses`. Open a session and add turns. Every call needs the `Upstage-Agent-Version: v2` header.

```bash
# 1. Start a session with the first message. The response "id" is the session ID
curl https://api.upstage.ai/v2/agents/sessions \
  -H "Authorization: Bearer $UPSTAGE_API_KEY" \
  -H "Upstage-Agent-Version: v2" \
  -H "Content-Type: application/json" \
  -d '{"agent_id": "YOUR_COWORKER_ID", "input": "Hello!"}'

# 2. Poll the latest turn until data[0].status is terminal
curl "https://api.upstage.ai/v2/agents/sessions/YOUR_SESSION_ID/turns?limit=1" \
  -H "Authorization: Bearer $UPSTAGE_API_KEY" \
  -H "Upstage-Agent-Version: v2"

# 3. Send a follow-up in the same session (returns 202, then poll again)
curl https://api.upstage.ai/v2/agents/sessions/YOUR_SESSION_ID/events \
  -H "Authorization: Bearer $UPSTAGE_API_KEY" \
  -H "Upstage-Agent-Version: v2" \
  -H "Content-Type: application/json" \
  -d '{"events": [{"type": "agent.session.input.message", "input": [{"role": "user", "content": [{"type": "input_text", "text": "Tell me more."}]}]}]}'
```

Turn status is one of `queued`, `in_progress`, `completed`, `failed`, `cancelled`, `requires_action`. The reply text is in `data[0].output`, in items of type `message`, in parts of type `output_text`. `requires_action` means the coworker is waiting for a person to approve an action in Studio: tell the user instead of polling forever. A coworker turn can take minutes, so poll every few seconds and set a generous timeout.

The coworker ID (`cw_...`) is in the coworker's "..." menu, under "Code".

## Search a collection

```bash
curl https://api.upstage.ai/v2/vector_stores/YOUR_COLLECTION_ID/search \
  -H "Authorization: Bearer $UPSTAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "YOUR_SEARCH_QUERY", "max_num_results": 5}'
```

Use the collection ID (starts with `scol_`) as the path ID. `data` holds the matched chunks with `file_id`, `filename`, `score`, and `content`. The endpoint is OpenAI SDK compatible: `client.vector_stores.search(vector_store_id=..., query=...)` with `base_url="https://api.upstage.ai/v2"`. A collection without a search index returns 404. The collection ID is in the Code dialog of that collection in Studio.

## Operating the web UI

Building is only possible in the web UI for now, so an assistant that controls a browser needs these rules.

- Find elements by `data-testid`. The IDs below are stable; visible text changes with the user's language.
- Some steps and sections exist only where a feature is enabled for the workspace. If a button described here is missing, tell the user instead of looking for a workaround.
- Sign-ins to connected apps (OAuth) and payment screens are for the user. Hand those over.
- Do not call `https://studio.upstage.ai/api/` from the page with scripts. Use the UI.

### File inputs

Most file inputs are hidden behind a button, and several exist on the same page for different purposes. Pick the input by its `data-testid`, never by position or by "the first file input". A schema JSON placed in the document input is uploaded as a document.

| `data-testid` | What it takes | Format |
|---|---|---|
| `header-upload-input` | Documents for a workflow to process (the Upload button in the workflow header) | PDF, images, Office files, video |
| `schema-toolbar-file-input` | Extract schema import (the schema toolbar of an Extract step) | JSON |
| `dc-schema-toolbar-file-input` | Classify schema import (the schema toolbar of a Classify step) | JSON |
| `workflow-import-json-input` | Whole workflow configuration import. Replaces the open workflow after a confirmation dialog | JSON |
| `my-workflows-bulk-import-input` | Import one or more workflows from the workflow list | JSON, ZIP |
| `gold-upload-input` | Answer set (gold) upload in the table view | YAML |
| `detail-gold-upload-input` | Answer set (gold) upload for the document open in the editor | YAML |
| `collection-upload-input` | Documents added to a collection | PDF, images, Office files (video where enabled) |
| `coworker-creation-knowledge-files-input` | Knowledge files for a new coworker (Build New Coworker page) | Any file |
| `coworker-settings-knowledge-files-input` | Knowledge files for an existing coworker (its settings, after Edit) | Any file |
| `skill-import-input` | Skill import in the skill form | Markdown, plain text |

### Build a workflow

The workflow list is at `https://studio.upstage.ai/agents`. `my-workflows-create-button` opens a dialog of starting points, each `pipeline-option-{id}`. Pick the one closest to the job: fewer steps to add means fewer connections to get wrong.

| `{id}` | Title on screen | Steps |
|---|---|---|
| `scratch` | From Scratch | Parse only |
| `parse` | Turn documents into text | Parse |
| `classify` | Organize documents by type | Parse → Classify |
| `extract` | Extract key information | Parse → Extract |
| `classify-extract` | Extract different data by document type | Parse → Classify → Extract |
| `instruct` | Instruct documents with AI | Parse → Extract → Instruct |

A workflow opens at `https://studio.upstage.ai/agents/{workflow_id}` with a generated name. Rename it by double-clicking the name in the header (`breadcrumb-agent-name`), or with "Edit name" in the "..." menu (`workflow-more-menu`, then `workflow-rename-button`).

These cover the page and block clicks until closed:

- A cookie banner. Which cookies to allow is the user's choice: ask, or choose `cookie-consent-reject` ("Reject non-essential").
- Feature introductions such as `coworker-intro-popover`. Close with `coworker-intro-dismiss`.
- Hints (`wizard-hint`) that point at the next thing to do.
- The first-run guide of a new workflow (`first-run-guide-popover`). From step 2 onward, `first-run-guide-previous` (the left chevron labeled "Previous") reopens the nearest earlier step with a visible target. `first-run-guide-close` closes it for this page; it returns on the next page load while the workflow is still empty. `first-run-guide-dismiss` ("Don't show again") turns it off for the user, so use it only if the user agrees.

1. Open the editor with `workflow-edit-button`, the sparkle icon in the middle of the header (next to the step tabs, or next to "Ready to run" in a new workflow). It opens the "Workflow editor" canvas with an "Add node" toolbar. Close it with `config-overlay-close-button`.
2. Add steps from the toolbar: `add-classify-button`, `add-extract-button`, `add-instruct-button`, `add-match-button`, `add-validate-button`, `add-merge-button`. Classify, Extract, and Instruct have a mode menu (`add-classify-mode-menu` and so on). You can also click the end marker of the canvas (`flow-end-node`) and pick a suggested next step (`next-step-ghost-{type}`).
3. Click a node to open its settings on the right. Nodes are `rf__node-{id}`, where the ID is `parse`, `import`, or `{type}-{name}`.
4. Connect nodes by dragging from the right handle of one node to the left handle of the next.

| Step | Node ID prefix | What it does |
|---|---|---|
| Import | `import` | Continues processing from results saved in a collection. Replaces Parse as the first step. Added from the start marker (`flow-start-node`), not the toolbar |
| Parse | `parse` | Converts your document (PDF, image, etc.) into machine-readable text. Always the first step, one per workflow |
| Classify | `classify` | Sorts documents by type, such as invoices, receipts, or contracts. Its split mode cuts one file into several documents |
| Extract | `extract` | Pulls out specific fields from documents, such as dates, amounts, or names. Uses a schema. See "Workflow and schema files" |
| Instruct | `instruct` | Uses AI to analyze, summarize, or make decisions about the content. Uses a prompt |
| Merge | `merge` | Merges split documents back into one. Adding it turns on Classify split |
| Match | `match` | Finds matching records and links them. Adding it with no Extract also adds an Extract |
| Validate | `validate` | Checks rules against upstream results and routes documents to green/yellow/red. See "When results look wrong" |
| Export | `export` | Saves results as searchable server-side tables (Collections) consumable by Match nodes in other workflows. Always last. Nothing connects out of it |

A rejected connection shows "Can't connect here" with one of these reasons. Values in braces are filled in on screen.

- "Parse is the starting step and can't receive a connection."
- "Steps can only connect in order (Parse → Classify → Extract)."
- "Enable both steps before connecting them."
- "A step can't connect to itself."
- "This connection would create a loop."
- "Each path can include only one {nodeType} step."
- "The imported Collection path already has a {nodeType} step. A path can have only one {nodeType} step."
- "Each branch can connect to only one next step."
- "This step already belongs to another path."
- "Merge requires Classify with split enabled."
- "Merge can only connect to Extract, Instruct, Validate, Export, or Match."
- "Merge must come after a Classify step in the path."
- "Match can only come right after an Extract step (directly or via a Merge of Extract steps)."
- "A Merge connected to Match can only receive Extract inputs."
- "A Match placed after a Merge can only connect to Instruct, Validate, or an HTTP Export."
- "A Match placed after a Merge can't connect to an Export that builds a Collection. Only an HTTP Export works here."
- "This Match's next step isn't supported after a Merge. Disconnect the next step before connecting the Merge."
- "An Export node holds one collection. Add another Export node for this route."
- "Imported document types can connect only to Extract, Instruct, Merge, Validate, or Export."

Each output holds one connection. Connecting a second one does not fail: the earlier connection is removed and a "Previous connection removed" notice appears. Check the canvas after every connection.

### Save and versions

- Edits are a draft and autosave a few seconds after each change. A draft is not a version.
- To save the draft as a version, open `config-version-selector` in the header and click `config-version-draft-save` ("Save"). It appears only while a draft exists.
- Saving checks the configuration first. If a step has a problem (marked on its node, for example an empty Instruct prompt), "Invalid configuration" appears with the list of problems and the draft stays a draft. The header shows "Draft" next to the version until the save succeeds.
- Running with a draft also saves it as a new version first.

### Run a workflow

- The Run button is `run-button`. When it is disabled, its tooltip says why. Fix what it says instead of retrying:
  - "Please upload files first to run your workflow"
  - "Please wait, your workflow is already running"
  - "Running is not available with API file source"
  - "Select a document to run your workflow"
  - "Switch to Details or Table view to run"
  - "Fix the configuration errors before running"
- In the table view, a run creates one job for every pair of selected file group and selected configuration version. The draft counts as one more version. 16 file groups with one saved version plus the draft create 32 jobs.
- Every job is billed work. In the table view, Run opens a confirmation dialog (`run-confirm-dialog`) whose title is the total number of jobs, followed by the versions and the file groups. Read it before confirming. If the count is not what you intended, close it and fix the selection.
- While a run is going, the header shows the step in progress instead of the step tabs. Do not decide a run is over from the header text. The run is over when its job in the Monitor view (`view-toggle-monitor`) has a final status: completed, failed, or cancelled.
- To stop a run, use `agent-run-cancel` in the header, then `agent-run-cancel-confirm`. Steps that already finished keep their results.
- If a job stays on the same step for a long time without progress, cancel it and tell the user instead of waiting indefinitely or starting another run.

### Read results

- Switch views with `view-toggle-detail` (one document at a time), `view-toggle-table` (one row per document), and `view-toggle-monitor` (jobs).
- In the detail view, pick a document in the file panel on the left. The step tabs in the header (`workflow-step-tab-{step}`) choose which step's result is shown on the right, with a formatted view and a JSON view.
- Results cite their source. Clicking a citation opens the original document at that place, which is the fastest way to check a value.

### When results look wrong

- To see why a job failed, open the workflow's Monitor view (`view-toggle-monitor`) and click the job. Its details (`job-detail-drawer`) show the failed step and the message.
- One file that holds several documents (for example two receipts scanned together) is treated as one document: Parse and Extract mix their values into one record. Put a Classify step with split before Extract so each document gets its own result.
- Validate compares values only along paths that run together. Comparing values from two different paths needs a Merge between them, and the run is blocked with a message that says so if it is missing.
- A Validate rule that refers to a value by a typed path, instead of picking it from the list, is not checked before the run. If every document comes out red, open one result and read which rule failed and which values it compared before changing anything else.
- A Validate step that is not connected to anything is left out of the saved configuration without a warning.

### Delete a workflow

- In the "..." menu (`workflow-more-menu`), choose `workflow-delete-button`, then confirm with `delete-workflow-dialog-confirm`.
- Uploaded files are kept unless you tick `delete-workflow-files-checkbox` in that dialog. It starts unticked.
- Finished results are kept, and so is the collection that runs fill with results (named after the workflow, ending in "results"). Delete that collection separately if the user wants everything gone.

### Workflow and schema files

Writing JSON and importing it is faster and less error prone than clicking through every field. Start from a file Studio exported whenever you can, change it, and import it back. These menus exist only where import and export are enabled.

**Whole workflow configuration**

- Export: the "..." menu in the workflow header (`workflow-more-menu`), then `workflow-export-json-button` ("Export workflow config"). The file has `"kind": "studio.workflow-config-draft"`, `"schemaVersion": 4`, and the steps, schemas, and connections under `workflowConfig` (step settings in `*Configuration` keys, connections in `pipelineEdges`).
- Import: the same menu, `workflow-import-json-button` ("Import workflow config"), or the file input `workflow-import-json-input`. A confirmation dialog follows, and the import replaces the configuration of the open workflow. The previous configuration stays in version history.
- The format is internal and changes between versions. Edit an exported file instead of writing one from scratch. A file whose steps and connections break the rules in "Build a workflow" is rejected.

**Extract schema**

Open the Extract step's settings, then import with the schema toolbar's "..." menu (`schema-toolbar-more-button`, then `schema-toolbar-import-button`) or the file input `schema-toolbar-file-input`. Export the current schema with `schema-toolbar-export-button` to see a real example.

```json
{
  "type": "json_schema",
  "json_schema": {
    "name": "invoice",
    "schema": {
      "type": "object",
      "properties": {
        "invoice_number": {
          "type": "string",
          "description": "Invoice number printed near the top"
        },
        "total_amount": {
          "type": "number",
          "description": "Grand total including tax"
        },
        "line_items": {
          "type": "array",
          "description": "One row per billed item",
          "items": {
            "type": "object",
            "properties": {
              "item": {
                "type": "string",
                "description": "Item name"
              },
              "amount": {
                "type": "number",
                "description": "Line amount"
              }
            }
          }
        }
      }
    }
  }
}
```

- The file must end in `.json`. Its file name becomes the step name.
- The import replaces the schema of the Extract step whose settings are open.
- A single value (string, number, boolean) becomes a field. An array of objects becomes a table, and its `items.properties` become the table's columns.
- At most 100 keys in total, where each field, each table, and each table column counts as one (the example above is 5). No name may start with `_`, and every table needs at least one column.
- `description` is optional but tells the model what to look for. Write it.

**Classify schema**

Open the Classify step's settings, then import with `dc-schema-toolbar-more-button`, then `dc-schema-toolbar-import-button`, or the file input `dc-schema-toolbar-file-input`. Export with `dc-schema-toolbar-export-button`.

```json
{
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "document-classify",
      "schema": {
        "type": "string",
        "oneOf": [
          {
            "const": "invoice",
            "description": "Bills from suppliers"
          },
          {
            "const": "receipt",
            "description": "Proof of payment"
          }
        ]
      }
    }
  }
}
```

- Each `oneOf` entry is one document type: `const` is its name and `description` says how to recognize it.
- To turn on split, add `"extra_body": { "split": true }` next to `response_format`. Export a Classify step that uses split to see the split criteria format.

### Build a coworker

- A new coworker: `https://studio.upstage.ai/coworkers/build` ("Build New Coworker"). Fields use the prefix `coworker-creation`: `coworker-creation-name`, `coworker-creation-role` ("Job title"), and `coworker-creation-instructions` ("Work instructions") are required. Submit with `coworker-creation-submit`. It is saved right away; there is no separate publish step.
- An existing coworker: `https://studio.upstage.ai/coworkers/{coworker_id}/settings` opens its settings. Click `coworker-settings-edit`, then the same fields use the prefix `coworker-settings`. Save with `coworker-settings-save` and confirm with `coworker-settings-save-confirm`. A save applies to new conversations for everyone who uses the coworker. Earlier versions are in `coworker-settings-version-history`.
- References: "Upload from computer" uses the knowledge file input in the table above, and "Choose from collections" picks collections. Picking a file uploads it to the account's Files (`https://studio.upstage.ai/files`) at once, even if the coworker is never saved, and deleting the coworker does not delete it. Keep track of what you upload, and remove files the user no longer needs from Files.
- Where enabled, the same picker is "Choose from collections and workflows" and has a "Workflows" tab for the AI workflows this coworker mainly uses. Up to 20 can be picked. Picking one turns on the workflow tool, and saving adds that workflow's default collection to the references; remove the collection if it is not needed. The save fails if a picked workflow was deleted or the user can no longer view it; such a workflow shows as "Workflow name unavailable" and can be unchecked in the same tab.
- Model: `{prefix}-model-select`. Scheduled tasks and triggers appear only where enabled.
- Where enabled, `https://studio.upstage.ai/coworkers/scheduled-tasks` lists all of the user's scheduled tasks (`scheduler-item`) and coworker triggers (`scheduled-trigger-item`) in one table, with search and filters by coworker and by type. Each row has an on/off switch (`scheduler-toggle`, `scheduled-trigger-toggle`) and a "..." menu to edit or delete. A trigger is created in its coworker's settings, not on this page; clicking a trigger row opens those settings.
- The coworker ID (`cw_...`) for the API is in the page URL (`/coworkers/{coworker_id}/chat/...`) and in the "..." menu (`coworker-conversation-menu`), under "Code" (`coworker-code-menu-item`). The code samples in that dialog can contain the user's real API key once one is selected. Do not paste them into code or chat; use `UPSTAGE_API_KEY` as in "Authentication".
- Chat with a coworker in the UI: type into `agent-chat-conversation-input` and press `agent-chat-conversation-send`. While the coworker is working, a stop button (`agent-chat-conversation-stop`) replaces the send button; the reply is finished when the send button is back.
- Delete a coworker: "..." menu, then `coworker-delete-menu-item` ("End collaboration with coworker"), then `coworker-delete-confirm`. Its reference files stay in Files.

### Connectors

- Connect apps at `https://studio.upstage.ai/settings/connectors`: each app has a Connect button, `connected-apps-connect-{app}` or `pipeline-connector-connect-{id}`. Connecting opens the app's own sign-in in a popup, which the user must complete.
- Give a coworker access in its Connectors section: open the Apps picker (`ai-coworker-connection-picker-connector`), tick `ai-coworker-connection-picker-dialog-connector-option-{app}`, then Apply.
- A coworker gets a connected app as a whole. There is no picker for a specific folder, spreadsheet, or mail label in the coworker settings, so name it in the work instructions (its name, URL, or ID) or in the chat. The folder or mailbox chosen for a trigger only decides what the trigger watches.

### Approvals

- A coworker asks for approval before actions that change something: sending mail or messages, writing to a connected app, running a workflow, creating schedules or triggers, and changing a coworker. Reading usually does not ask.
- Read the target and the values shown in the approval card before approving. If the card does not make clear which file or record is changed, ask the user instead of approving.
- The chat's run mode can be "Ask for permission", "Auto", or "Skip permissions". Do not switch it to skip approvals unless the user asks you to.

### Coworker memory

A coworker can save rules from earlier conversations ("Preference", "Correction", "Naming rule", "Don't", "Decision", "Work fact") and apply them later, which changes its output without any visible instruction. Where memory is enabled, check one coworker's memories from its "..." menu (`coworker-memory-menu-item`, read only), or all of them at `https://studio.upstage.ai/settings/memory` ("Personal memory" and "Coworker memory"), where they can be edited or deleted. Memories belong to the signed-in user: you cannot see what a coworker remembers for other people. The list does not show which conversation saved a memory. If a coworker follows a rule you cannot find in its instructions, check there.

## More

Not covered here yet: skills, projects, publishing to the library, and managing collections. Ask the user before changing those.

- API reference: https://console.upstage.ai/docs/studio-and-agents/agents/overview
- This file: https://studio.upstage.ai/llms.txt
