---
title: "Create Document"
method: POST
path: "/v1/documents"
tags: ["documents"]
---

# Create Document

`POST /v1/documents`

## Request

- Base URL: `https://api.versionstory.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.versionstory.com/v1/documents`
- Auth: HTTP bearer

Start a document's history with a file as its first version; an edit of a file is kept as a version
only once the file is one. Send the file's bytes as multipart/form-data in the file field, or name a
file you uploaded (or an edit's files.clean) by file_id, as a form field or a JSON body: the first
version is that file as it is, so its listing and paragraph ids stay the ones you read. Each upload starts a new
document; a file named by file_id becomes a version once, and naming it again is 409 CONFLICT. An edit's files.clean
can be named while the edit is still being applied; one whose edit failed is refused.

## Headers

- `X-Customer-Id` string, nullable: Id of one of your customers (created in the developer dashboard) to attribute this request's credit usage to. Billing and limits stay organization-wide.

## Request body

- DocumentFromFileRequest: POST /v1/documents's JSON body: a Document whose first version is a file you hold.
  - `file_id` string, required: A file you uploaded (POST /v1/files) or an edit's files.clean: the version is that file as it is, not a copy, so the paragraph ids read from it carry over.
  - `name` string, nullable: The Document's name; the file's by default.

## Response `201`

Successful Response

- DocumentDetail: A Document with every version.
  - `document_id` string, required: The Document's id.
  - `name` string, nullable: The Document's name: the name it was given, else its first file's.
  - `version_count` integer, required: How many versions it has.
  - `latest_version` VersionReference: A version a response points at: a parent, a Document's latest version, an edit's version. Sent back as it is, it names the version in a request (VersionLocator reads its version_id).
    - `document_id` string, required: The Document the version belongs to.
    - `version_id` string, required: The version's id.
    - `index` integer, required: The version's position among its Document's versions in the order they were made, counted from 0: the first version, v1, is index 0. It shows the order and names nothing: deleting a version moves the ones after it down, so a request names a version by its version_id.
    - `label` string, required: The app's label for the version, such as v2, or v2(a) for one of competing drafts, which the user names it by. It comes from the version graph, so a sibling added later can relabel it.
  - `created_at` string, nullable: When the Document was made: ISO 8601 in UTC, to the second: 2026-10-21T18:22:31Z.
  - `updated_at` string, nullable: When the Document last changed: ISO 8601 in UTC, to the second: 2026-10-21T18:22:31Z.
  - `metadata` object: Your own string values by key, stored and returned as they were sent.
  - `versions` Version[]: Every version, in index order.
    - `document_id` string, required: The Document the version belongs to.
    - `version_id` string, required: The version's id.
    - `index` integer, required: The version's position among its Document's versions in the order they were made, counted from 0: the first version, v1, is index 0. It shows the order and names nothing: deleting a version moves the ones after it down, so a request names a version by its version_id.
    - `label` string, required: The app's label for the version, such as v2, or v2(a) for one of competing drafts, which the user names it by. It comes from the version graph, so a sibling added later can relabel it.
    - `name` string, required: The version's name, which the app shows: its own once renamed, else its file's.
    - `files` Files, required: A document's files, as an edit and a version carry them: its clean document and its redline.
      - `clean` FileResource, required: A file: GET /v1/files/{file_id} answers with it, and every response that carries a document embeds it. It never carries version fields: a version holds its file.
        - `file_id` string, required
        - `file_name` string, required
        - `status` 'processing' | 'ready' | 'failed', required: The state of the formats this request asked for: processing while one is still being produced (poll again after Retry-After), ready when every one is in downloads, failed when the document cannot be used (error) or a requested format will never be produced.
        - `downloads` FormatDownload[], nullable: One per requested format that is ready.
          - `format` string, required
          - `url` string, required: Signed; it expires at expires_at.
          - `file_name` string, required
          - `expires_at` string, required: ISO 8601 in UTC, to the second: 2026-10-21T18:22:31Z.
          - `metadata_report` MetadataReport: What a clean found in one download and did to each kind of hidden data.
            - `format` string, required: docx, xlsx, pptx, pdf or zip.
            - `preset` string, required: standard or strict.
            - `items` MetadataReportItem[], required
              - …
            - `warnings` MetadataReportWarning[], required
              - …
            - `files` integer, nullable: For a zip, how many documents inside it were cleaned.
          - `content_base64` string, nullable: Only in an edit's or a creation's files, with inline=true: the file's bytes, base64-encoded.
        - `pending_formats` string[], nullable: Requested formats still being produced.
        - `failed_formats` string[], nullable: Requested formats that will never be produced.
        - `error` ComparisonErrorDescriptor
          - `code` string, required
          - `upstream_code` string, nullable
      - `comparison` FileResource: A file: GET /v1/files/{file_id} answers with it, and every response that carries a document embeds it. It never carries version fields: a version holds its file.
        - `file_id` string, required
        - `file_name` string, required
        - `status` 'processing' | 'ready' | 'failed', required: The state of the formats this request asked for: processing while one is still being produced (poll again after Retry-After), ready when every one is in downloads, failed when the document cannot be used (error) or a requested format will never be produced.
        - `downloads` FormatDownload[], nullable: One per requested format that is ready.
          - `format` string, required
          - `url` string, required: Signed; it expires at expires_at.
          - `file_name` string, required
          - `expires_at` string, required: ISO 8601 in UTC, to the second: 2026-10-21T18:22:31Z.
          - `metadata_report` MetadataReport: What a clean found in one download and did to each kind of hidden data.
            - `format` string, required: docx, xlsx, pptx, pdf or zip.
            - `preset` string, required: standard or strict.
            - `items` MetadataReportItem[], required
              - …
            - `warnings` MetadataReportWarning[], required
              - …
            - `files` integer, nullable: For a zip, how many documents inside it were cleaned.
          - `content_base64` string, nullable: Only in an edit's or a creation's files, with inline=true: the file's bytes, base64-encoded.
        - `pending_formats` string[], nullable: Requested formats still being produced.
        - `failed_formats` string[], nullable: Requested formats that will never be produced.
        - `error` ComparisonErrorDescriptor
          - `code` string, required
          - `upstream_code` string, nullable
    - `parents` VersionReference[]: The versions it was made from: one, or each version a merge combined, primary first.
      - `document_id` string, required: The Document the version belongs to.
      - `version_id` string, required: The version's id.
      - `index` integer, required: The version's position among its Document's versions in the order they were made, counted from 0: the first version, v1, is index 0. It shows the order and names nothing: deleting a version moves the ones after it down, so a request names a version by its version_id.
      - `label` string, required: The app's label for the version, such as v2, or v2(a) for one of competing drafts, which the user names it by. It comes from the version graph, so a sibling added later can relabel it.
    - `author` string, nullable: Who its changes are attributed to: an edit's author, an upload's author_name.
    - `description` string, nullable: What changed: an edit's description, an upload's description.
    - `created_at` string, required: When the version was made: ISO 8601 in UTC, to the second: 2026-10-21T18:22:31Z.
    - `metadata` object: Your own string values by key, stored and returned as they were sent.

## Other responses

- `400`: The request is invalid: INVALID_REQUEST when a parameter or the body fails validation, otherwise the code of the operation's own refusal.

---

[API](https://skmtc.dev/versionstory/apis/version-story-public-api.md) - [All operations](https://skmtc.dev/versionstory/apis/version-story-public-api/llms.txt) - [OpenAPI document](https://skmtc.dev/versionstory/apis/version-story-public-api/revisions/994ce15faf32?raw)
