---
title: "create_upload_session"
method: POST
path: "/v1/workspaces/{workspace_id}/upload-sessions"
tags: ["files"]
---

# create_upload_session

`POST /v1/workspaces/{workspace_id}/upload-sessions`

## Request

- Base URL: `https://ndi-api.nace.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://ndi-api.nace.ai/v1/workspaces/{workspace_id}/upload-sessions`
- Auth: one of:
  - API key in header `X-API-Key`
  - API key in header `X-Upload-Token`
- TypeScript: <https://skmtc.dev/nace/apis/ndi-service/docs/v1/workspaces/:workspace_id/upload-sessions/post.ts> (a function with typed inputs; its response is checked against the schema)

Open a chunked upload for a large file. Cost class: **fast**.

Same auth as ``upload_file`` — an ``X-API-Key`` or a short-lived
``X-Upload-Token`` grant — but redeemed exactly once, here. Every later
request against the session (parts, status, complete, abort) presents
the ``session_token`` this returns instead, in the same ``X-Upload-Token``
header.

Upload every part with ``PUT .../upload-sessions/{session_id}/parts/{n}``
at exactly ``chunk_size`` bytes (the final part may be shorter), then
call ``POST .../complete``. ``GET`` the session at any point to see which
parts have already landed, e.g. after a dropped connection.

Args:
    session: Database session.
    body: Destination path, declared total size, and conflict policy —
        the same fields ``upload_file`` takes, minus the bytes.
    upload_auth: The resolved workspace context, plus the upload grant
        when the caller redeemed one instead of sending an API key.
    idempotency_key: Replaying with the same key returns the original
        session rather than opening a second one.

Returns:
    A :class:`CreateUploadSessionResponse` with the session token.

Raises:
    PlatformError: ``file_too_large`` when ``total_size_bytes`` exceeds
        the effective byte cap.
    PlatformError: ``access_denied`` when a path-pinned grant is used to
        create a session at a different path.
    PlatformError: ``unauthorized`` when a single-use grant is replayed.

## Path parameters

- `workspace_id` string, uuid, required: Workspace identifier.

## Headers

- `Idempotency-Key` string, nullable: Replaying a request with the same key returns the original job with 200, never a second job. The key is kept with the job for replay and never cleared, so it must not contain personal data.

## Request body

- CreateUploadSessionRequest: Body for ``POST /workspaces/{workspace_id}/upload-sessions``. Same destination/conflict/labelling contract as ``UploadFileRequest`` — this is the chunked entry point to the exact same ledger write, for a file large enough (or a link flaky enough) that one unretried multipart POST is the wrong shape. ``total_size_bytes`` is required and checked byte-for-byte at ``complete``: nothing here is trusted on faith.
  - `path` string, required: Destination path inside the workspace. Relative, must not start with '/', and must not use the platform-reserved '.workspace' subtree.
  - `total_size_bytes` integer, required: Exact total size of the file, across every part.
  - `source_modified_at` string, date-time, nullable: Filesystem modification time reported by the uploading client. Informational only: callers control this value, so it is not audit evidence.
  - `labels` object: Caller-supplied metadata, stored verbatim.
  - `graph_inclusion` 'auto' | 'include' | 'exclude': Whether this file joins the knowledge-graph corpus. 'auto' follows the workspace's KG exclusion rules; 'include'/'exclude' override them for this file.
  - `on_conflict` 'reject' | 'new_version': 'reject' raises path_conflict at complete-time when a file already exists at this path. 'new_version' increments the version and stamps replaced_at on the previous one.
  - `ttl_seconds` integer: How long the session accepts parts before it expires unfinished.

## Response `201`

Successful Response

- CreateUploadSessionResponse: A newly created upload session, ready to accept parts. ``session_token`` is sent as ``X-Upload-Token`` on every subsequent request against this session (parts, status, complete, abort) — the upload grant or API key used to create the session is not sent again.
  - `session_id` string, uuid, required
  - `session_token` string, required: Send in the X-Upload-Token header of every subsequent request.
  - `chunk_size` integer, required: Upload every part at exactly this size, except a shorter final part.
  - `total_parts` integer, required: ceil(total_size_bytes / chunk_size) — the parts complete() expects.
  - `expires_at` string, date-time, required: When the session stops accepting parts.

## Other responses

- `400`: Bad Request
- `401`: Unauthorized
- `402`: Payment Required
- `403`: Forbidden
- `404`: Not Found
- `409`: Conflict
- `410`: Gone
- `413`: Request Entity Too Large
- `422`: Unprocessable Entity
- `424`: Failed Dependency
- `429`: Too Many Requests
- `500`: Internal Server Error
- `501`: Not Implemented
- `503`: Service Unavailable
- `504`: Gateway Timeout

---

[API](https://skmtc.dev/nace/apis/ndi-service.md) - [All operations](https://skmtc.dev/nace/apis/ndi-service/llms.txt) - [OpenAPI document](https://skmtc.dev/nace/apis/ndi-service/revisions/d9d1f8ec2207?raw)
