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

# Process Document

> Upload a document for processing. Convert PDFs, DOCX files, and images to Markdown or run custom extractors.

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.doctly.ai/api/v1/documents \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -F "file=@document.pdf" \
    -F "accuracy=ultra"
  ```
</RequestExample>

**POST** `/api/v1/documents`

Upload a document to convert it to Markdown or process it with a custom extractor. The API supports PDF, DOCX, and image files up to 100MB.

## Request

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token authentication. Example: `Bearer YOUR_API_KEY`
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `multipart/form-data`
</ParamField>

### Body Parameters

<ParamField body="file" type="file">
  The document file to process. Supported formats: PDF, DOCX, PNG, JPG, JPEG, WEBP, GIF.

  <Note>Provide either `file` or `url`, not both.</Note>
</ParamField>

<ParamField body="url" type="string">
  URL to download the document from. The file will be fetched and processed.

  <Note>Provide either `file` or `url`, not both.</Note>
</ParamField>

<ParamField body="accuracy" type="string" default="lite">
  Processing accuracy level for Markdown conversion:

  * `lite` — Fast processing with great accuracy (default)
  * `ultra` — Highest accuracy for complex documents
</ParamField>

<ParamField body="extractor_id" type="string">
  UUID of a custom extractor to use instead of standard Markdown conversion.
</ParamField>

<ParamField body="page_separator" type="boolean" default="true">
  Include page break markers (`---`) in the Markdown output.
</ParamField>

<ParamField body="skip_images" type="boolean" default="false">
  When `true`, images are not extracted or transcribed from the document.
</ParamField>

<ParamField body="callback_url" type="string">
  Webhook URL to receive a POST request when processing completes.
</ParamField>

## EU Endpoint

For data residency requirements, an EU-based endpoint is available at `api.eu.doctly.ai`. Documents processed through this endpoint are stored and processed entirely within the European Union.

<Warning>
  The EU endpoint only supports the `url` parameter. Direct file uploads using the `file` parameter are not available on this endpoint.
</Warning>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.eu.doctly.ai/api/v1/documents \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -F "url=https://example.com/report.pdf" \
    -F "accuracy=ultra"
  ```

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

  url = "https://api.eu.doctly.ai/api/v1/documents"
  headers = {"Authorization": "Bearer YOUR_API_KEY"}

  data = {
      "url": "https://example.com/report.pdf",
      "accuracy": "ultra"
  }

  response = requests.post(url, headers=headers, data=data)
  print(response.json())
  ```
</CodeGroup>

## Example Requests

### Convert to Markdown

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.doctly.ai/api/v1/documents \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -F "file=@document.pdf" \
    -F "accuracy=ultra"
  ```

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

  url = "https://api.doctly.ai/api/v1/documents"
  headers = {"Authorization": "Bearer YOUR_API_KEY"}

  with open("document.pdf", "rb") as f:
      files = {"file": ("document.pdf", f, "application/pdf")}
      data = {"accuracy": "ultra"}
      response = requests.post(url, headers=headers, files=files, data=data)

  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const formData = new FormData();
  formData.append('file', fileInput.files[0]);
  formData.append('accuracy', 'ultra');

  const response = await fetch('https://api.doctly.ai/api/v1/documents', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY'
    },
    body: formData
  });

  const data = await response.json();
  ```
</CodeGroup>

### Convert from URL

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.doctly.ai/api/v1/documents \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -F "url=https://example.com/report.pdf" \
    -F "accuracy=lite"
  ```

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

  url = "https://api.doctly.ai/api/v1/documents"
  headers = {"Authorization": "Bearer YOUR_API_KEY"}

  data = {
      "url": "https://example.com/report.pdf",
      "accuracy": "lite"
  }

  response = requests.post(url, headers=headers, data=data)
  print(response.json())
  ```
</CodeGroup>

### Use Custom Extractor

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.doctly.ai/api/v1/documents \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -F "file=@invoice.pdf" \
    -F "extractor_id=987fcdeb-a654-3210-9876-543210987654"
  ```

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

  url = "https://api.doctly.ai/api/v1/documents"
  headers = {"Authorization": "Bearer YOUR_API_KEY"}

  with open("invoice.pdf", "rb") as f:
      files = {"file": ("invoice.pdf", f, "application/pdf")}
      data = {"extractor_id": "987fcdeb-a654-3210-9876-543210987654"}
      response = requests.post(url, headers=headers, files=files, data=data)

  print(response.json())
  ```
</CodeGroup>

## Response

<Expandable title="Document Object">
  <ResponseField name="id" type="string">
    Unique identifier (UUID) for the document
  </ResponseField>

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

  <ResponseField name="file_size" type="integer">
    Size of the file in bytes
  </ResponseField>

  <ResponseField name="page_count" type="integer">
    Number of pages in the document (null until processed)
  </ResponseField>

  <ResponseField name="status" type="string">
    Processing status: `PENDING`, `PROCESSING`, `COMPLETED`, `FAILED`, or `EXPIRED`
  </ResponseField>

  <ResponseField name="accuracy" type="string">
    Selected accuracy level: `lite` or `ultra`
  </ResponseField>

  <ResponseField name="extractor_id" type="string">
    UUID of the extractor used (if any)
  </ResponseField>

  <ResponseField name="extractor" type="object">
    Extractor details (if using custom extraction)
  </ResponseField>

  <ResponseField name="created_at" type="string">
    ISO 8601 timestamp of creation
  </ResponseField>
</Expandable>

## Example Responses

<ResponseExample>
  ```json 200 OK — Markdown Conversion theme={null}
  {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "file_name": "document.pdf",
    "file_size": 1048576,
    "page_count": 12,
    "status": "PENDING",
    "accuracy": "ultra",
    "created_at": "2024-03-21T13:45:00Z"
  }
  ```

  ```json 200 OK — With Extractor theme={null}
  {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "file_name": "invoice.pdf",
    "file_size": 524288,
    "page_count": 2,
    "status": "PENDING",
    "extractor_id": "987fcdeb-a654-3210-9876-543210987654",
    "extractor": {
      "id": "987fcdeb-a654-3210-9876-543210987654",
      "name": "Invoice Extractor",
      "slug": "invoice-extractor",
      "cost_type": "PER_PAGE",
      "cost_credits": 5
    },
    "created_at": "2024-03-21T13:45:00Z"
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "detail": "Unsupported file type '.exe'. Supported types are: .pdf, .docx, .png, .jpg, .jpeg, .webp, .gif"
  }
  ```

  ```json 400 Bad Request — Password Protected theme={null}
  {
    "detail": "Cannot process password-protected PDF files"
  }
  ```

  ```json 413 Payload Too Large theme={null}
  {
    "detail": "File size exceeds maximum limit of 100MB"
  }
  ```

  ```json 422 Unprocessable Entity theme={null}
  {
    "detail": [
      {
        "loc": ["body", "file"],
        "msg": "field required",
        "type": "value_error.missing"
      }
    ]
  }
  ```
</ResponseExample>

## Webhooks

When `callback_url` is provided, a POST request is sent when processing completes:

```json theme={null}
{
  "document_id": "123e4567-e89b-12d3-a456-426614174000",
  "file_name": "document.pdf",
  "status": "COMPLETED"
}
```

<Note>
  Webhooks retry up to 3 times with 5-second delays if delivery fails. URLs must be HTTPS and publicly accessible.
</Note>

## Next Steps

After creating a document, poll [Get Document](/api-reference/documents/get) until `status` is `COMPLETED` or `FAILED`. The `output_file_url` provides a signed download link for the result.

<CodeGroup>
  ```bash cURL theme={null}
  # Poll until complete
  DOC_ID="123e4567-e89b-12d3-a456-426614174000"
  while true; do
    RESP=$(curl -s https://api.doctly.ai/api/v1/documents/$DOC_ID \
      -H "Authorization: Bearer YOUR_API_KEY")
    STATUS=$(echo $RESP | jq -r '.status')
    echo "Status: $STATUS"
    if [ "$STATUS" = "COMPLETED" ] || [ "$STATUS" = "FAILED" ]; then
      echo $RESP | jq '.output_file_url'
      break
    fi
    sleep 5
  done
  ```

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

  doc_id = "123e4567-e89b-12d3-a456-426614174000"
  headers = {"Authorization": "Bearer YOUR_API_KEY"}

  while True:
      resp = requests.get(
          f"https://api.doctly.ai/api/v1/documents/{doc_id}",
          headers=headers
      ).json()
      
      print(f"Status: {resp['status']}")
      
      if resp["status"] in ("COMPLETED", "FAILED"):
          if resp["status"] == "COMPLETED":
              print(f"Download: {resp['output_file_url']}")
          break
      
      time.sleep(5)
  ```
</CodeGroup>
