Sessions and Turns
To call an AI employee conversationally, pick a Deployment, create a Session, and send Turns. A Session is fixed to one Deployment, and earlier turns carry over.
Listing Deployments
GET /v1/deploymentsreturns the Deployments this key can call. (deployments:read)GET /v1/deployments/{deployment_id}shows the name, version, status (ACTIVE,RETIRED), model tier (model_tier), allowed tools, the declared result contract (outputs), the allowed input MIME types, and the call rate limits.- Actual model names are not exposed. Only the tier is shown.
Creating a Session
POST /v1/deployments/{deployment_id}/sessions creates a Session and its first Turn in one step. It needs sessions:write and turns:write, and Idempotency-Key is required.
- The server returns
202after it has stored the acceptance. A dropped connection does not lose an accepted Turn. - The response (receipt) has
session_id,turn_id,status,idempotency_expires_at,status_url, andevents_url. TheLocationheader is the relative address of the Turn detail. - The same key with the same body as an earlier request returns the same receipt. The same key with a different body returns
409 IDEMPOTENCY_CONFLICT. - A receipt is kept for at least 24 hours (
idempotency_expires_at) and until that Turn has ended.
Follow-up Turns
Send new input to the same Session with POST /v1/sessions/{session_id}/turns.
- A Session runs only one Turn at a time. Sending before the previous Turn ends returns
409 SESSION_BUSY. There is no queue, so confirm that the Turn ended before you send. - Sending to a closed Session returns
409 SESSION_CLOSED. - Different Sessions run in parallel, subject to the Project’s concurrent execution limit.
{
"error": {
"code": "SESSION_BUSY",
"message": "The session already has an active turn",
"retryable": false,
"request_id": "0b7b8f0e-0000-4000-8000-000000000004",
"details": {}
}
}Read, cancel, close, delete
| Path | Description |
|---|---|
GET /v1/sessions/{session_id} | Session status (ACTIVE, CLOSED) and the active Turn |
GET /v1/sessions/{session_id}/turns | List Turns with an after and limit cursor |
GET /v1/sessions/{session_id}/turns/{turn_id} | Turn status, usage, error, results (outputs), and next_cursor |
GET /v1/sessions/{session_id}/items | Public Item history |
POST /v1/sessions/{session_id}/turns/{turn_id}/cancel | Cancel a Turn. 202 for a new cancel, 200 if it already ended |
POST /v1/sessions/{session_id}/close | Close an idle Session. 409 if it is running |
DELETE /v1/sessions/{session_id} | Accept a delete (202). Reading content is an immediate 404 afterward |
Turn statuses are QUEUED, RUNNING, REQUIRES_ACTION, CANCEL_REQUESTED, SUCCEEDED, FAILED, CANCELED, and EXPIRED. SUCCEEDED, FAILED, CANCELED, and EXPIRED are terminal. A failed Turn was still accepted successfully, so read the reason from error.code in the response.
- A cancel stores the intent first, and effects that already completed are not undone. If a cancel and a completion overlap, whichever finished first is the final state.
- For deletion, see the delete section of limits and data.
- You can add
X-Correlation-Id(letters, digits, dot, underscore, colon, hyphen, 1 to 128 characters) to a request for tracing on your side. A bad format returns400 INVALID_CORRELATION_ID.
Input format
{
"input": [
{"type": "TEXT", "text": "Compare the attached report with last month."},
{"type": "FILE", "file_id": "d3c1b8a0-0000-4000-8000-000000000003"}
],
"metadata": {"request_ref": "example-001"},
"limits": {"queue_seconds": 600, "execution_seconds": 3600, "max_su": 50}
}inputis an array ofTEXTandFILEitems. TEXT cannot be empty or only whitespace. Multiple TEXT items are run in order, joined with line breaks.- The
file_idof aFILEitem is a value you got by uploading withPOST /v1/filesfirst. A file that is already bound to a Session or Run cannot be reused by another execution, so upload it again. metadatatakes at most 16 keys. A key is 1 to 64 characters and a string value is at most 512 characters.limitsis optional.queue_secondsis 1 to 600,execution_secondsis 1 to 3600, andmax_suis above 0 and at most 100 (up to 8 decimal places). A value above the cap is not silently reduced. It is rejected with422 LIMIT_OUT_OF_RANGE.- The JSON request body is at most 64KiB in UTF-8. Larger bodies return
413 INPUT_TOO_LARGE. Undeclared fields such as model, prompt, or tools return422 INVALID_INPUT. - Files are 20MiB each, up to 20 per input. The allowed MIME types differ per Deployment.
Output format
Each item of outputs in a Turn detail is one of these four types.
[
{"id": "o1", "key": "answer", "type": "TEXT", "visibility": "FINAL", "availability": "READY",
"item_id": "i1", "text": "Done."},
{"id": "o2", "key": "summary", "type": "JSON", "visibility": "FINAL", "availability": "READY",
"schema_ref": "summary", "data": {"count": 3}},
{"id": "o3", "key": "report", "type": "FILE", "visibility": "FINAL", "availability": "READY",
"artifact_id": "a1", "filename": "report.txt", "mime_type": "text/plain",
"size_bytes": 1024, "sha256": "..."},
{"id": "o4", "key": "reference", "type": "URL", "visibility": "FINAL", "availability": "READY",
"url": "https://example.com/report", "access": "UNKNOWN", "expires_at": null}
]| type | Content |
|---|---|
TEXT | The sanitized body text. A body over 64KiB is delivered as a file. |
JSON | data validated against the schema set at publication. If it is large, a file is provided through artifact_id instead of data. |
FILE | artifact_id, file name, MIME type, size, and sha256. See the download section of Workflows and Runs. |
URL | An external HTTPS link url and its access mode (access). UpServe neither stores it nor guarantees its availability. |
visibilityisINTERMEDIATE,PARTIAL, orFINAL. Final results areFINAL.- Items whose
availabilityisFAILEDorEXPIREDcome with anerror. - The result list is at most 100 items and 256KiB per response. Continue with
afterandnext_cursor. - A successful Turn must contain every result marked
requiredat publication. If one is missing or malformed, the Turn closes as a failure, not a success.
Usage
The usage field of Turn and Run responses has two states.
{"status": "PENDING", "total_su": null}- When
FINAL,total_suis the settled amount. - When
PENDING, the cost is not decided yet andtotal_suisnull. It is not 0 SU. Reading again later returns the settled value. GET /v1/usagenarrows by one ofkey_id,deployment_id,session_id,turn_id, orrun_idand shows settled amounts, reserved amounts, and the breakdown by item. (usage:read)preparationsin the same response shows the preparation costs of organization works separately, with the same status, settled amount, reserved amount, and breakdown by item. An ongoing preparation does not change the executionstatusortotal_su. For a key granted every Deployment and holdingworks:read, the Project’s total spend is the execution total plus thepreparationstotal.- With
works:readon the key it covers every preparation in the Project. Without it, only the preparations that key accepted.key_idnarrows it to the preparations that key accepted. - With
deployment_id,session_id,turn_id, orrun_id,preparationsisnull, because a preparation belongs to none of them. It is not 0 SU.
- With
Resolving questions (RequiredAction)
This is the feature where a Turn stops at REQUIRES_ACTION when the AI employee must ask the caller something during execution. It is not offered in the current Private Beta. It is a service-wide setting and cannot be turned on per Project. For now, published Deployments do not include the question tool, and a workflow execution that comes to need a human answer or approval ends with ACTION_REQUIRED_UNSUPPORTED. The contract below applies once the feature is offered.
- Answer a question the caller may answer (
CALLER) withPOST /v1/sessions/{session_id}/actions/{action_id}/resolve. It needsactions:resolveandIdempotency-Key. - Workflow approvals require separate
actions:approvedelegation. Collect the human decision in your own UI and submit it through the Run action API. See workflow approvals. - After the answer deadline it returns
409 ACTION_EXPIRED.
curl -s -X POST "$BASE/v1/sessions/$SESSION_ID/actions/$ACTION_ID/resolve" \
-H "Authorization: Bearer $UPSERVE_API_KEY" \
-H "Idempotency-Key: answer-0001" \
-H "Content-Type: application/json" \
-d '{"answers":[{"question_id":"q1","text":"Use the Seoul office."}]}'