> ## Documentation Index
> Fetch the complete documentation index at: https://docs.medisync.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Update Medical Document

> Replace or add a document on an appointment

## Overview

Uploads a new document file for an appointment. Depending on what is already attached, the endpoint either **replaces** an existing document or **adds** a new one:

* If the appointment has a consent document, that consent document is replaced.
* Otherwise, if the appointment has exactly one document, that document is replaced.
* Otherwise (multiple documents and none is a consent document), the new file is simply added.

A new document record with a new `_id` is created on every call. Accepted file types are **PDF, PNG, JPEG, JPG and WEBP**.

<Warning>
  This operation is **not atomic**. When a document is replaced, the old file is deleted from storage and the database first, and the new file is uploaded afterward. If the new upload fails, the old document is already gone.
</Warning>

<Info>
  This endpoint is plan-gated by the `documents.attach_additional` feature. Accounts whose plan does not include it receive `403 FEATURE_NOT_AVAILABLE`.
</Info>

## Authentication

<ParamField header="Authorization" type="string" required>
  Bearer token for authenticated access. The doctor's ID is extracted from the JWT token.

  ```
  Authorization: Bearer your_jwt_token_here
  ```
</ParamField>

## Path Parameters

<ParamField path="id" type="string" required>
  The appointment ID (ObjectId) whose document should be replaced or created. The appointment must belong to the authenticated doctor, otherwise a `403 Forbidden` is returned.
</ParamField>

## Body Parameters

The request must be sent as `multipart/form-data`.

<ParamField body="document" type="file" required>
  The replacement document file. Allowed types: `application/pdf`, `image/png`, `image/jpeg`, `image/jpg`, `image/webp`.
</ParamField>

<ParamField body="title" type="string">
  Optional display title for the document. If omitted, the uploaded file's original filename is used.
</ParamField>

## Response

<ResponseField name="success" type="boolean">
  Whether the operation was successful
</ResponseField>

<ResponseField name="data" type="object">
  The newly stored document record

  <Expandable title="Document Object">
    <ResponseField name="_id" type="string">
      Unique document identifier (a new ObjectId is generated on each call)
    </ResponseField>

    <ResponseField name="appointment_id" type="string">
      The appointment this document is attached to
    </ResponseField>

    <ResponseField name="document_url" type="string">
      Opaque storage URL for the document
    </ResponseField>

    <ResponseField name="document_key" type="string">
      Storage key. Format: `{appointment_id}_{timestamp}_{original_filename}`
    </ResponseField>

    <ResponseField name="doctor_id" type="string">
      ObjectId of the uploading doctor (the authenticated user)
    </ResponseField>

    <ResponseField name="file_name" type="string">
      Original filename of the uploaded file
    </ResponseField>

    <ResponseField name="title" type="string">
      Display title (defaults to the original filename)
    </ResponseField>

    <ResponseField name="file_type" type="string">
      Inferred short file type (e.g. `pdf`, `png`, `jpeg`, `webp`)
    </ResponseField>

    <ResponseField name="mime_type" type="string">
      MIME type of the stored file
    </ResponseField>

    <ResponseField name="source" type="string">
      Origin of the document. `upload` for files uploaded through this endpoint
    </ResponseField>

    <ResponseField name="extraction_status" type="string">
      Text-extraction state. New documents start as `pending`
    </ResponseField>

    <ResponseField name="include_in_context" type="boolean">
      Whether the document is included when building context. Defaults to `true`
    </ResponseField>

    <ResponseField name="createdAt" type="string">
      Creation timestamp (ISO 8601)
    </ResponseField>

    <ResponseField name="updatedAt" type="string">
      Last-update timestamp (ISO 8601)
    </ResponseField>

    <ResponseField name="__v" type="number">
      Internal record version
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="error" type="string">
  Error message (only present when `success` is `false`)
</ResponseField>

<Note>
  Additional fields may be present on the document object and should be treated as opaque.
</Note>

## Example Request

<RequestExample>
  ```bash cURL theme={null}
  curl -X PUT \
    'https://app.medisync.me/api/documents/665e0b9d3f2a1c4d8e7f0a12' \
    -H 'Authorization: Bearer your_jwt_token_here' \
    -F 'document=@updated_report.pdf' \
    -F 'title=Updated Report'
  ```

  ```javascript JavaScript theme={null}
  const formData = new FormData();
  formData.append('document', file); // PDF or image file
  formData.append('title', 'Updated Report'); // optional

  const response = await fetch(
    'https://app.medisync.me/api/documents/665e0b9d3f2a1c4d8e7f0a12',
    {
      method: 'PUT',
      headers: { Authorization: 'Bearer your_jwt_token_here' },
      body: formData,
    }
  );

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

  ```python Python theme={null}
  import requests

  files = {'document': open('updated_report.pdf', 'rb')}
  data = {'title': 'Updated Report'}  # optional

  response = requests.put(
      'https://app.medisync.me/api/documents/665e0b9d3f2a1c4d8e7f0a12',
      headers={'Authorization': 'Bearer your_jwt_token_here'},
      files=files,
      data=data,
  )

  result = response.json()
  ```
</RequestExample>

## Example Response

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "_id": "66a3f1c2b8e4d21f9c0a7b40",
      "appointment_id": "665e0b9d3f2a1c4d8e7f0a12",
      "document_url": "https://storage.medisync.me/665e0b9d3f2a1c4d8e7f0a12_1719924000000_updated_report.pdf",
      "document_key": "665e0b9d3f2a1c4d8e7f0a12_1719924000000_updated_report.pdf",
      "doctor_id": "6512a7c9e4b0a1f2d3c45678",
      "file_name": "updated_report.pdf",
      "title": "updated_report.pdf",
      "file_type": "pdf",
      "mime_type": "application/pdf",
      "source": "upload",
      "extraction_status": "pending",
      "include_in_context": true,
      "createdAt": "2026-07-02T15:20:00.000Z",
      "updatedAt": "2026-07-02T15:20:00.000Z",
      "__v": 0
    }
  }
  ```
</ResponseExample>

## Error Responses

<ResponseExample>
  ```json 400 Error deleting old document theme={null}
  {
    "success": false,
    "error": "Error deleting old document"
  }
  ```

  ```json 400 Unsupported file type theme={null}
  {
    "success": false,
    "error": "Unsupported file type. Allowed: PDF, PNG, JPEG, WEBP."
  }
  ```

  ```json 400 No file uploaded theme={null}
  {
    "success": false,
    "error": "No file uploaded"
  }
  ```

  ```json 404 Appointment not found theme={null}
  {
    "success": false,
    "error": "Appointment not found"
  }
  ```

  ```json 403 Forbidden (appointment owned by another doctor) theme={null}
  {
    "success": false,
    "error": "Forbidden"
  }
  ```

  ```json 403 Feature not in plan theme={null}
  {
    "success": false,
    "code": "FEATURE_NOT_AVAILABLE",
    "feature": "documents.attach_additional",
    "message": "This feature is not included in your current plan."
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "success": false,
    "message": "Unauthorized"
  }
  ```
</ResponseExample>
