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

# POST /api/v1/jobs — Create a Document Parsing Job

> POST /api/v1/jobs creates a hosted parse or compare job. Requires an Idempotency-Key header and a Bearer auth token. Supports URL and binary file inputs.

The `POST /api/v1/jobs` endpoint accepts a document and starts a parse or compare job on FileRouter's hosted infrastructure. You can submit documents in two ways: as a JSON body with a public URL, or as a raw binary body for direct file uploads. The endpoint always returns immediately with a job ID and an initial status — you then poll `GET /api/v1/jobs/{jobId}` to retrieve the result.

```
POST https://filerouter.dev/api/v1/jobs
```

<Note>
  The TypeScript SDK (`FileRouterClient`) handles all headers and body serialization automatically, including idempotency key generation. Use the REST API directly only if you're integrating from a non-TypeScript environment.
</Note>

## Required Headers

<ParamField header="Authorization" type="string" required>
  Bearer token authentication. Format: `Bearer <your-api-key>`. See [Authentication](/api/authentication) for details.
</ParamField>

<ParamField header="Idempotency-Key" type="string" required>
  A unique key (8–255 characters) that makes this request safely retryable. If you send the same `Idempotency-Key` a second time with the same request body, the API returns the original job rather than creating a new one. Use a random UUID or a deterministic key derived from your request content.
</ParamField>

## Request Formats

<Tabs>
  <Tab title="JSON Body (URL input)">
    Use this format when your document is accessible at a public HTTP or HTTPS URL. Set `Content-Type: application/json` and send the job parameters as a JSON object.

    ### Content-Type

    ```
    Content-Type: application/json
    ```

    ### Body Parameters

    <ParamField body="operation" type="string" required>
      The job operation to perform. Must be either `"parse"` (process with one provider) or `"compare"` (process with multiple providers side-by-side).
    </ParamField>

    <ParamField body="source.url" type="string" required>
      The HTTP or HTTPS URL of the document to process. The URL must be publicly accessible from FileRouter's infrastructure.
    </ParamField>

    <ParamField body="outputs" type="array" required>
      An array of output format IDs to request (at least one required). Example: `["markdown"]`. The available output IDs depend on the provider's declared capabilities.
    </ParamField>

    <ParamField body="pages" type="array">
      An optional array of one-based page numbers to process. When omitted, FileRouter processes the entire document. Example: `[1, 2, 3]`.
    </ParamField>

    <ParamField body="provider" type="string">
      The provider ID to use for a `parse` operation (e.g., `"llamaparse"`, `"mistral-ocr"`). When omitted, FileRouter uses a default provider. Only valid when `operation` is `"parse"`.
    </ParamField>

    <ParamField body="providers" type="array">
      An array of provider IDs to use for a `compare` operation. Requires at least one entry. Only valid when `operation` is `"compare"`.
    </ParamField>

    <ParamField body="providerOptions" type="object">
      Optional namespaced provider-specific options. Keys are provider IDs; values are objects containing that provider's native options. Example: `{ "llamaparse": { "premium_mode": true } }`.
    </ParamField>

    <ParamField body="includeRaw" type="boolean">
      When `true`, the completed job result includes the full raw response from the provider alongside the normalized outputs. Defaults to `false`. Use this only when you need provider-native data — raw responses can be large, especially for image-heavy documents.
    </ParamField>

    ### Example

    ```bash title="Create a URL-based parse job" theme={null}
    curl https://filerouter.dev/api/v1/jobs \
      --request POST \
      --header "Authorization: Bearer $FILEROUTER_API_KEY" \
      --header "Content-Type: application/json" \
      --header "Idempotency-Key: parse-annual-report-2024" \
      --data '{
        "operation": "parse",
        "outputs": ["markdown"],
        "source": {
          "url": "https://example.com/annual-report-2024.pdf"
        },
        "provider": "llamaparse"
      }'
    ```
  </Tab>

  <Tab title="Binary Body (file upload)">
    Use this format to upload a document directly. Set `Content-Type` to the document's MIME type and send the raw file bytes as the request body. All job parameters travel as request headers with the `X-FileRouter-*` prefix.

    FileRouter accepts uploads up to **100 MB**. The request body streams directly into storage — it is never buffered in memory during ingress.

    ### Content-Type

    Set `Content-Type` to the document's MIME type, for example:

    ```
    Content-Type: application/pdf
    ```

    FileRouter resolves the MIME type in this order: the explicit `Content-Type` you provide, inference from the filename extension in `X-FileRouter-Filename`, then a fallback to `application/octet-stream`.

    ### Upload Headers

    <ParamField header="X-FileRouter-Filename" type="string" required>
      The URL-encoded filename of the document, including its extension (e.g., `annual-report-2024.pdf` → `annual-report-2024.pdf`). FileRouter uses this for MIME inference and for naming the stored object. Encode non-ASCII characters with `encodeURIComponent`.
    </ParamField>

    <ParamField header="X-FileRouter-Operation" type="string" required>
      The operation to perform: `"parse"` or `"compare"`.
    </ParamField>

    <ParamField header="X-FileRouter-Outputs" type="string" required>
      A comma-separated list of output format IDs to request. Example: `markdown` or `markdown,pages`.
    </ParamField>

    <ParamField header="X-FileRouter-Pages" type="string">
      Optional comma-separated list of one-based page numbers to process. Example: `1,2,3`. When omitted, FileRouter processes the entire document.
    </ParamField>

    <ParamField header="X-FileRouter-Provider" type="string">
      The provider ID to use for a `parse` operation. Only used when `X-FileRouter-Operation` is `parse`.
    </ParamField>

    <ParamField header="X-FileRouter-Providers" type="string">
      A comma-separated list of provider IDs for a `compare` operation. Only used when `X-FileRouter-Operation` is `compare`.
    </ParamField>

    <ParamField header="X-FileRouter-Provider-Options" type="string">
      Optional URL-encoded JSON string of namespaced provider options. Example: `encodeURIComponent(JSON.stringify({ llamaparse: { premium_mode: true } }))`.
    </ParamField>

    <ParamField header="X-FileRouter-Include-Raw" type="string">
      Set to `"true"` to include the full raw provider response in the result. Defaults to `"false"`.
    </ParamField>

    ### Example

    ```bash title="Upload a local PDF file" theme={null}
    curl https://filerouter.dev/api/v1/jobs \
      --request POST \
      --header "Authorization: Bearer $FILEROUTER_API_KEY" \
      --header "Content-Type: application/pdf" \
      --header "Idempotency-Key: upload-report-q3-001" \
      --header "X-FileRouter-Filename: q3-report.pdf" \
      --header "X-FileRouter-Operation: parse" \
      --header "X-FileRouter-Outputs: markdown" \
      --header "X-FileRouter-Provider: llamaparse" \
      --data-binary @q3-report.pdf
    ```
  </Tab>
</Tabs>

## Responses

### 202 Accepted — Job created

The API accepted your request and created a new job. The job is now `queued` or already `running`.

```json title="202 response body" theme={null}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued"
}
```

<Expandable title="Response fields">
  <ResponseField name="id" type="string" required>
    The UUID of the newly created job. Use this value to poll `GET /api/v1/jobs/{jobId}`.
  </ResponseField>

  <ResponseField name="status" type="string" required>
    The initial job status. One of `"queued"`, `"running"`, `"complete"`, or `"failed"`.
  </ResponseField>
</Expandable>

### 200 OK — Idempotent replay

You sent an `Idempotency-Key` that already exists for your account with the same request body. The API returns the original job without starting a new workflow. The response shape is identical to 202.

```json title="200 idempotent replay body" theme={null}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued"
}
```

## Error Responses

<Accordion title="400 Bad Request">
  The request body or headers failed validation. The response body describes the specific field or constraint that failed.

  ```json title="400 error body" theme={null}
  {
    "status": 400,
    "code": "validation_error",
    "detail": "outputs: Required",
    "title": "Bad Request",
    "type": "https://filerouter.dev/problems/validation-error",
    "instance": "/api/v1/jobs",
    "request_id": "01J2Y9QX3MXJQHD2YQ9N7J93M4"
  }
  ```
</Accordion>

<Accordion title="401 Unauthorized">
  The `Authorization` header is missing, invalid, or the API key has been disabled or expired. See [Authentication](/api/authentication) for details.

  ```json title="401 error body" theme={null}
  {
    "status": 401,
    "code": "unauthorized",
    "detail": "Missing FileRouter API key.",
    "title": "Unauthorized",
    "type": "https://filerouter.dev/problems/unauthorized",
    "instance": "/api/v1/jobs",
    "request_id": "01J2Y9QX3MXJQHD2YQ9N7J93M4"
  }
  ```
</Accordion>

<Accordion title="409 Conflict — Idempotency conflict">
  You sent the same `Idempotency-Key` with a **different** request body. FileRouter will not overwrite an existing idempotency record. Use a new, unique key for a different request.

  ```json title="409 error body" theme={null}
  {
    "status": 409,
    "code": "idempotency_conflict",
    "detail": "A job with this idempotency key already exists with different parameters.",
    "title": "Conflict",
    "type": "https://filerouter.dev/problems/idempotency-conflict",
    "instance": "/api/v1/jobs",
    "request_id": "01J2Y9QX3MXJQHD2YQ9N7J93M4"
  }
  ```
</Accordion>

<Accordion title="413 Content Too Large">
  The uploaded file exceeds the 100 MB request body limit. Split your document or contact support about large-file upload sessions.
</Accordion>

<Accordion title="500 Internal Server Error">
  An unexpected server-side error occurred. The response includes a `request_id` — include it when contacting support.
</Accordion>
