> This page is for API Reference.

> Sarvam Voice Agents (Samvaad) is a voice AI agent platform for Indian languages: build, test and deploy AI calling agents on your own phone numbers, from ₹3 a minute. Start here: https://docs.sarvam.ai/voice-agents/why-voice-agents.
> For clean Markdown of any page, append `.md` to the page URL.
> For a complete documentation index, see https://docs.sarvam.ai/llms.txt. For a section index, append `/llms.txt` to any section URL (for example https://docs.sarvam.ai/voice-agents/llms.txt).
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sarvam.ai/_mcp/server.

# Split Document

POST https://api.sarvam.ai/doc-ai/v1/job/split
Content-Type: multipart/form-data

Creates a Split job. Split divides a bundle of pages into separate child documents, each with its original page numbers and a `document_ref` you can reuse in other requests.

**Methods:** `fixed_pages` (equal page groups), `page_ranges` (you give the boundaries), `triggers` (split at QR codes, barcodes, blank pages, text or photos) and `semantic` (the model finds the boundaries).

**Input:** send `file` (PDF, PNG, JPEG or TIFF; repeat the field for up to 10 documents) or `upload_ids`, plus a JSON `request`.

**Processing:** the job runs asynchronously. Poll `GET /doc-ai/v1/job/{job_id}/status` until it is `completed`, then fetch `GET /doc-ai/v1/job/{job_id}/results`. `completed` means processing finished, not that every segment passed review: check each segment's `review.required`.

Reference: https://docs.sarvam.ai/api-reference/doc-ai/job/split

## Authentication

- `api-subscription-key` header (required) — API Key authentication via header

## Request

### Headers

- `Idempotency-Key` (string, optional) — Retry the same submission without creating another job. The same key with the same request returns the original job (`idempotency_replayed: true`); the same key with a changed request returns `409 IDEMPOTENCY_CONFLICT`.

### Body (multipart/form-data)

This endpoint expects a multipart form with multiple files.

- `file` (files, optional) — Document file(s): PDF, PNG, JPEG or TIFF. Repeat the part for up to 10 documents with the same config.
- `upload_ids` (string, optional) — Comma-separated upload IDs returned by `POST /doc-ai/v1/job/upload`, with or instead of `file`.
- `request` (DocAISplitJobRequest, required)

## Response

### 201

Job created. With `request.execution` set to `auto`, a single document that finishes in time instead returns `200` with the results object.

- `job_id` (string, required) — The unique identifier of the job. For a batch, the parent job.
- `status` (string, required) — Initial status, `accepted`.
- `status_url` (string, required) — Path to poll for status.
- `results_url` (string, required) — Path to fetch results from once the job is `completed`.
- `run_id` (string, optional) — The unique identifier of the job run.
- `idempotency_replayed` (boolean, optional) — `true` when the response replays an earlier request with the same `Idempotency-Key`.
- `resolved_budget` (map from string to any, optional) — Limits applied to the job, such as `deadline_at` and `max_model_calls`.
- `resolved_limits` (DocAICapabilityLimits, optional)
- `documents` (list of DocAIBatchDocument, optional) — Batch submissions only: one entry per document, each with its own job.

## Errors

### 400 Bad Request Error

Invalid request, including unknown fields, bad enum values and mutually exclusive options

- `code` (string, required) — Machine-readable error code, e.g. `INVALID_REQUEST`, `UNAUTHORIZED`, `NOT_FOUND`, `RESULTS_NOT_READY`, `IDEMPOTENCY_CONFLICT`, `PAGE_RANGE_INVALID`, `UNSUPPORTED_CAPABILITY`, `UNSUPPORTED_INPUT` or `TOO_MANY_DOCUMENTS`.
- `message` (string, required) — Human-readable summary.
- `retryable` (boolean, required) — Whether retrying the same request can succeed.
- `detail` (string, optional) — Explanation specific to this occurrence.
- `pointer` (string, optional) — JSON pointer to the offending field, e.g. `/config/scope`.
- `request_id` (string, optional) — Request identifier to quote when reporting an issue.
- `status` (integer, optional) — HTTP status code.
- `title` (string, optional) — Short title of the HTTP status.
- `job_status` (string, optional) — Present on `RESULTS_NOT_READY`, the current status of the job.

### 401 Unauthorized Error

Missing or invalid API key

- `code` (string, required) — Machine-readable error code, e.g. `INVALID_REQUEST`, `UNAUTHORIZED`, `NOT_FOUND`, `RESULTS_NOT_READY`, `IDEMPOTENCY_CONFLICT`, `PAGE_RANGE_INVALID`, `UNSUPPORTED_CAPABILITY`, `UNSUPPORTED_INPUT` or `TOO_MANY_DOCUMENTS`.
- `message` (string, required) — Human-readable summary.
- `retryable` (boolean, required) — Whether retrying the same request can succeed.
- `detail` (string, optional) — Explanation specific to this occurrence.
- `pointer` (string, optional) — JSON pointer to the offending field, e.g. `/config/scope`.
- `request_id` (string, optional) — Request identifier to quote when reporting an issue.
- `status` (integer, optional) — HTTP status code.
- `title` (string, optional) — Short title of the HTTP status.
- `job_status` (string, optional) — Present on `RESULTS_NOT_READY`, the current status of the job.

### 404 Not Found Error

Input or upload not found

- `code` (string, required) — Machine-readable error code, e.g. `INVALID_REQUEST`, `UNAUTHORIZED`, `NOT_FOUND`, `RESULTS_NOT_READY`, `IDEMPOTENCY_CONFLICT`, `PAGE_RANGE_INVALID`, `UNSUPPORTED_CAPABILITY`, `UNSUPPORTED_INPUT` or `TOO_MANY_DOCUMENTS`.
- `message` (string, required) — Human-readable summary.
- `retryable` (boolean, required) — Whether retrying the same request can succeed.
- `detail` (string, optional) — Explanation specific to this occurrence.
- `pointer` (string, optional) — JSON pointer to the offending field, e.g. `/config/scope`.
- `request_id` (string, optional) — Request identifier to quote when reporting an issue.
- `status` (integer, optional) — HTTP status code.
- `title` (string, optional) — Short title of the HTTP status.
- `job_status` (string, optional) — Present on `RESULTS_NOT_READY`, the current status of the job.

### 409 Conflict Error

`IDEMPOTENCY_CONFLICT`: the `Idempotency-Key` was already used with a different request

- `code` (string, required) — Machine-readable error code, e.g. `INVALID_REQUEST`, `UNAUTHORIZED`, `NOT_FOUND`, `RESULTS_NOT_READY`, `IDEMPOTENCY_CONFLICT`, `PAGE_RANGE_INVALID`, `UNSUPPORTED_CAPABILITY`, `UNSUPPORTED_INPUT` or `TOO_MANY_DOCUMENTS`.
- `message` (string, required) — Human-readable summary.
- `retryable` (boolean, required) — Whether retrying the same request can succeed.
- `detail` (string, optional) — Explanation specific to this occurrence.
- `pointer` (string, optional) — JSON pointer to the offending field, e.g. `/config/scope`.
- `request_id` (string, optional) — Request identifier to quote when reporting an issue.
- `status` (integer, optional) — HTTP status code.
- `title` (string, optional) — Short title of the HTTP status.
- `job_status` (string, optional) — Present on `RESULTS_NOT_READY`, the current status of the job.

### 422 Unprocessable Entity Error

`PAGE_RANGE_INVALID`, `UNSUPPORTED_CAPABILITY` or `UNSUPPORTED_INPUT`

- `code` (string, required) — Machine-readable error code, e.g. `INVALID_REQUEST`, `UNAUTHORIZED`, `NOT_FOUND`, `RESULTS_NOT_READY`, `IDEMPOTENCY_CONFLICT`, `PAGE_RANGE_INVALID`, `UNSUPPORTED_CAPABILITY`, `UNSUPPORTED_INPUT` or `TOO_MANY_DOCUMENTS`.
- `message` (string, required) — Human-readable summary.
- `retryable` (boolean, required) — Whether retrying the same request can succeed.
- `detail` (string, optional) — Explanation specific to this occurrence.
- `pointer` (string, optional) — JSON pointer to the offending field, e.g. `/config/scope`.
- `request_id` (string, optional) — Request identifier to quote when reporting an issue.
- `status` (integer, optional) — HTTP status code.
- `title` (string, optional) — Short title of the HTTP status.
- `job_status` (string, optional) — Present on `RESULTS_NOT_READY`, the current status of the job.

## Types

### DocAICapabilityLimits

- `max_source_pages` (integer, optional) — Maximum pages in one source document.
- `max_source_bytes` (integer, optional) — Maximum size of one source document in bytes.
- `max_categories` (integer, optional) — Maximum categories in one request.
- `max_segments` (integer, optional) — Maximum segments a Split can return.
- `max_inline_result_bytes` (integer, optional) — Largest result returned inline.

### DocAIBatchDocument

- `index` (integer, optional) — Position of the document in the submission, starting at 1.
- `filename` (string, optional)
- `job_id` (string, optional) — Job for this document.
- `status` (string, optional)
- `status_url` (string, optional)
- `results_url` (string, optional)

## Examples

**Request**

```json
{
  "file": [],
  "request": {
    "config": {
      "method": "fixed_pages",
      "classify_segments": false,
      "pages_per_segment": 2
    }
  }
}
```

**Response**

```json
{
  "job_id": "01a17998-07ef-7040-800d-745a95140cf5",
  "status": "accepted",
  "status_url": "/doc-ai/v1/job/01a17998-07ef-7040-800d-745a95140cf5/status",
  "results_url": "/doc-ai/v1/job/01a17998-07ef-7040-800d-745a95140cf5/results",
  "run_id": "run_01a17998-07ef-7040-800d-745a95140cf5",
  "idempotency_replayed": false
}
```

**SDK Code**

```python
from sarvamai import SarvamAI

client = SarvamAI(api_subscription_key="YOUR_SARVAM_API_KEY")

with open("./document.pdf", "rb") as f:
    job = client.doc_ai.split(
        file=[("document.pdf", f, "application/pdf")],
        request={
            "config": {
                "method": "fixed_pages",
                "pages_per_segment": 2,
                "classify_segments": False,
            },
        },
    )
print("job created:", job.job_id, "| status:", job.status)

```

```typescript
import fs from "fs";
import { SarvamAIClient } from "sarvamai";

const client = new SarvamAIClient({ apiSubscriptionKey: process.env.SARVAM_API_KEY });

const job = await client.docAi.split({
  file: [fs.createReadStream("./document.pdf")],
  request: {
    config: { method: "fixed_pages", pages_per_segment: 2, classify_segments: false },
  },
});
console.log("job created:", job.job_id, "| status:", job.status);

```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.sarvam.ai/doc-ai/v1/job/split"

	payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"request\"\r\n\r\n{\n  \"config\": {\n    \"method\": \"fixed_pages\",\n    \"classify_segments\": false,\n    \"pages_per_segment\": 2\n  }\n}\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"upload_ids\"\r\n\r\n\r\n-----011000010111000001101001--\r\n")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("api-subscription-key", "<apiSubscriptionKey>")
	req.Header.Add("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.sarvam.ai/doc-ai/v1/job/split")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["api-subscription-key"] = '<apiSubscriptionKey>'
request["Content-Type"] = 'multipart/form-data; boundary=---011000010111000001101001'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"request\"\r\n\r\n{\n  \"config\": {\n    \"method\": \"fixed_pages\",\n    \"classify_segments\": false,\n    \"pages_per_segment\": 2\n  }\n}\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"upload_ids\"\r\n\r\n\r\n-----011000010111000001101001--\r\n"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.sarvam.ai/doc-ai/v1/job/split")
  .header("api-subscription-key", "<apiSubscriptionKey>")
  .header("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")
  .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"request\"\r\n\r\n{\n  \"config\": {\n    \"method\": \"fixed_pages\",\n    \"classify_segments\": false,\n    \"pages_per_segment\": 2\n  }\n}\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"upload_ids\"\r\n\r\n\r\n-----011000010111000001101001--\r\n")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.sarvam.ai/doc-ai/v1/job/split', [
  'multipart' => [
    [
        'name' => 'request',
        'contents' => '{
  "config": {
    "method": "fixed_pages",
    "classify_segments": false,
    "pages_per_segment": 2
  }
}'
    ]
  ]
  'headers' => [
    'api-subscription-key' => '<apiSubscriptionKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.sarvam.ai/doc-ai/v1/job/split");
var request = new RestRequest(Method.POST);
request.AddHeader("api-subscription-key", "<apiSubscriptionKey>");
request.AddParameter("multipart/form-data; boundary=---011000010111000001101001", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"request\"\r\n\r\n{\n  \"config\": {\n    \"method\": \"fixed_pages\",\n    \"classify_segments\": false,\n    \"pages_per_segment\": 2\n  }\n}\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"upload_ids\"\r\n\r\n\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "api-subscription-key": "<apiSubscriptionKey>",
  "Content-Type": "multipart/form-data; boundary=---011000010111000001101001"
]
let parameters = [
  [
    "name": "request",
    "value": "{
  \"config\": {
    \"method\": \"fixed_pages\",
    \"classify_segments\": false,
    \"pages_per_segment\": 2
  }
}"
  ],
  [
    "name": "upload_ids",
    "value": 
  ]
]

let boundary = "---011000010111000001101001"

var body = ""
var error: NSError? = nil
for param in parameters {
  let paramName = param["name"]!
  body += "--\(boundary)\r\n"
  body += "Content-Disposition:form-data; name=\"\(paramName)\""
  if let filename = param["fileName"] {
    let contentType = param["content-type"]!
    let fileContent = String(contentsOfFile: filename, encoding: String.Encoding.utf8)
    if (error != nil) {
      print(error as Any)
    }
    body += "; filename=\"\(filename)\"\r\n"
    body += "Content-Type: \(contentType)\r\n\r\n"
    body += fileContent
  } else if let paramValue = param["value"] {
    body += "\r\n\r\n\(paramValue)"
  }
}

let request = NSMutableURLRequest(url: NSURL(string: "https://api.sarvam.ai/doc-ai/v1/job/split")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```