> ## 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.

# Safely Retry Parse and Compare Jobs with Idempotency Keys

> Use idempotency keys to retry hosted parse and compare jobs safely. FileRouter returns the existing result — no double-billing, no duplicate processing.

Network failures happen. A request might time out before you receive the response, leaving you unsure whether FileRouter created a job or not. Idempotency keys solve this: you send the same key with a retry, and FileRouter returns the existing job instead of starting a new one. You get the same result, you're not billed twice, and your document isn't processed again.

## How idempotency works

Every job creation request to the hosted FileRouter API must include an `Idempotency-Key` header. The value must be between **8 and 255 characters**.

When FileRouter receives a request:

<Steps>
  <Step title="Check for an existing job">
    FileRouter looks up whether a job with that idempotency key already exists under your API key.
  </Step>

  <Step title="Return the existing job (if found)">
    If a matching job exists **and** the request parameters are identical, FileRouter returns the existing job with HTTP `200`. No new processing starts.
  </Step>

  <Step title="Create a new job (if not found)">
    If no matching job exists, FileRouter creates a new job and returns HTTP `202 Accepted`.
  </Step>

  <Step title="Reject mismatched requests">
    If a job exists for that key but the request parameters differ (different file, different operation, different outputs), FileRouter returns **HTTP 409 Conflict**. FileRouter will not silently process a different document under the same key.
  </Step>
</Steps>

<Warning>
  Sending the same idempotency key with different inputs — a different file, a different operation, or different outputs — results in a **409 Conflict** response. FileRouter treats the key as a contract: one key, one job. Start a new key for a new request.
</Warning>

## Automatic key generation in the TypeScript SDK

The TypeScript SDK generates a UUID idempotency key automatically for every `parse` and `compare` call. You don't need to do anything for normal usage — each call gets its own unique key.

```ts title="Default behavior — SDK generates a UUID automatically" theme={null}
import { FileRouterClient } from '@file_router/sdk'

const client = new FileRouterClient({ apiKey: process.env.FILEROUTER_API_KEY })

// The SDK generates a fresh UUID idempotency key for this call
const result = await client.parse(file, {
  outputs: ['markdown'],
  provider: 'llamaparse',
})
```

## Explicit idempotency keys for safe retries

Supply your own `idempotencyKey` when you want to safely retry the exact same request — for example, after a network failure where you didn't receive a response:

```ts title="Explicit idempotency key for retry safety" theme={null}
import { FileRouterClient } from '@file_router/sdk'

const client = new FileRouterClient({ apiKey: process.env.FILEROUTER_API_KEY })

const idempotencyKey = 'invoice-parse-2024-01-15-doc42'

const result = await client.parse(file, {
  outputs: ['markdown', 'tables'],
  provider: 'mistral-ocr',
  idempotencyKey,
})
```

The same `idempotencyKey` option is available on `compare` calls through `HostedCompareOptions`:

```ts title="Explicit idempotency key on a compare call" theme={null}
const result = await client.compare(file, {
  outputs: ['markdown'],
  idempotencyKey: 'quarterly-report-compare-2024-q1',
})
```

## Raw HTTP usage

If you're calling the API directly without the SDK, set the `Idempotency-Key` header on every job creation request:

```http title="Job creation request with Idempotency-Key header" theme={null}
POST /api/v1/jobs HTTP/1.1
Authorization: Bearer <your-api-key>
Content-Type: application/json
Idempotency-Key: invoice-parse-2024-01-15-doc42

{
  "operation": "parse",
  "outputs": ["markdown"],
  "provider": "llamaparse",
  "source": { "url": "https://example.com/invoice.pdf" }
}
```

## Retry loop pattern

Use the same key across retries so that a successful-but-unacknowledged request doesn't create a duplicate job:

```ts title="Retry loop with a stable idempotency key" theme={null}
import { FileRouterClient } from '@file_router/sdk'

const client = new FileRouterClient({ apiKey: process.env.FILEROUTER_API_KEY })

async function parseWithRetry(file: File, maxAttempts = 3) {
  // Generate the key once, before the retry loop
  const idempotencyKey = crypto.randomUUID()

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await client.parse(file, {
        outputs: ['markdown'],
        provider: 'llamaparse',
        // Use the same key on every attempt
        idempotencyKey,
      })
    } catch (error) {
      if (attempt === maxAttempts) throw error
      // Wait before retrying
      await new Promise((resolve) => setTimeout(resolve, attempt * 1000))
    }
  }
}
```

<Tip>
  Generate your idempotency key **before** the first attempt and reuse it across all retries. If you generate a new key on each retry, you lose the safety guarantee — each retry becomes a new job.
</Tip>

## Key format guidelines

<Info>
  The `Idempotency-Key` header value must be between 8 and 255 characters after trimming whitespace. UUIDs (36 characters) work well. Descriptive keys like `report-parse-2024-01-15-user123` are also valid and make logs easier to read.
</Info>

| Format                 | Example                                | Valid |
| ---------------------- | -------------------------------------- | ----- |
| UUID v4                | `550e8400-e29b-41d4-a716-446655440000` | ✓     |
| ULID                   | `01J2Y9QX3MXJQHD2YQ9N7J93M4`           | ✓     |
| Descriptive string     | `invoice-42-parse-2024-01-15`          | ✓     |
| Too short (\< 8 chars) | `abc123`                               | ✗     |
| Too long (> 255 chars) | *(long string)*                        | ✗     |
