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

# Submit a Book

> Upload a deal package and start parsing in one request.

```http theme={null}
POST /submit-book
Content-Type: multipart/form-data
```

This endpoint creates a Book, attaches every uploaded file, and starts parsing in the background. The response arrives before parsing finishes.

Use one request per deal. Files submitted together are classified and parsed as one package.

## Request

<ParamField body="files" type="file[]" required>
  One or more financial documents. Send multiple files by repeating the `files` field.
</ParamField>

<ParamField body="book_name" type="string" required>
  A recognizable name for the deal. The parser may later replace it with the extracted business name.
</ParamField>

<ParamField body="description" type="string">
  Optional context stored on the Book.
</ParamField>

<ParamField body="webhook_url" type="string">
  Optional HTTPS URL that receives the final parse status. Omit it when you plan to poll.
</ParamField>

PDF is the normal format for bank statements, credit reports, applications, and tax forms. Supported document images can also be classified. Files that cannot be classified remain visible on the Book with a failed or unsupported status.

## Example

<CodeGroup>
  ```python Python theme={null}
  from pathlib import Path
  import requests

  API_BASE = "https://api.lendpathway.com/api"
  TOKEN = "pat_your_token_here"

  paths = [Path("jan.pdf"), Path("feb.pdf"), Path("application.pdf")]

  with requests.Session() as session:
      session.headers["Authorization"] = f"Bearer {TOKEN}"

      opened = [path.open("rb") for path in paths]
      try:
          files = [
              ("files", (path.name, handle, "application/pdf"))
              for path, handle in zip(paths, opened)
          ]
          response = session.post(
              f"{API_BASE}/submit-book",
              files=files,
              data={
                  "book_name": "Smith Auto Body",
                  "webhook_url": "https://example.com/webhooks/pathway",
              },
              timeout=120,
          )
          response.raise_for_status()
          print(response.json())
      finally:
          for handle in opened:
              handle.close()
  ```

  ```javascript JavaScript theme={null}
  import { openAsBlob } from "node:fs";

  const API_BASE = "https://api.lendpathway.com/api";
  const TOKEN = "pat_your_token_here";

  const form = new FormData();
  form.set("book_name", "Smith Auto Body");
  form.set("webhook_url", "https://example.com/webhooks/pathway");

  for (const filename of ["jan.pdf", "feb.pdf", "application.pdf"]) {
    const file = await openAsBlob(filename, { type: "application/pdf" });
    form.append("files", file, filename);
  }

  const response = await fetch(`${API_BASE}/submit-book`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${TOKEN}`,
    },
    body: form,
  });

  if (!response.ok) {
    throw new Error(`${response.status}: ${await response.text()}`);
  }

  console.log(await response.json());
  ```

  ```bash cURL theme={null}
  curl --fail-with-body \
    "https://api.lendpathway.com/api/submit-book" \
    -H "Authorization: Bearer pat_your_token_here" \
    -F "book_name=Smith Auto Body" \
    -F "webhook_url=https://example.com/webhooks/pathway" \
    -F "files=@jan.pdf;type=application/pdf" \
    -F "files=@feb.pdf;type=application/pdf" \
    -F "files=@application.pdf;type=application/pdf"
  ```
</CodeGroup>

Do not set `Content-Type` manually in Python or JavaScript. The HTTP client adds the multipart boundary when it builds the body.

## Response

```python theme={null}
class SubmitBookResponse(BaseModel):
    book_id: str
    status: str
    message: str
```

```json theme={null}
{
  "book_id": "99cc93e6-1f3f-42b1-9fe4-ba5a95be9c78",
  "status": "processing",
  "message": "Processing started"
}
```

Save `book_id`. You use it to read status, analytics, exports, and embeds.

<Note>
  `status: "processing"` confirms that the asynchronous submission flow was accepted. Continue checking the Book until its stored `parse_status` becomes `completed`, `failed`, or `cancelled`.
</Note>

## Completion webhook

When `webhook_url` is present, Pathway sends one JSON `POST` after the parse reaches its final database status.

```python theme={null}
class ParseWebhook(BaseModel):
    book_id: str
    status: Literal["completed", "failed", "cancelled"]
    book_url: str
    error: str | None = None
```

```json theme={null}
{
  "book_id": "99cc93e6-1f3f-42b1-9fe4-ba5a95be9c78",
  "status": "completed",
  "book_url": "https://app.lendpathway.com/books/99cc93e6-1f3f-42b1-9fe4-ba5a95be9c78"
}
```

Return a `2xx` response quickly and move any result fetching into your own background job.

<Warning>
  Completion webhooks are currently unsigned and are sent once. Treat the Book ID as a lookup key, then call the authenticated Book endpoint to confirm the current status before moving a deal forward.
</Warning>

## Poll instead

Call `GET /books/{book_id}` every few seconds and stop on a terminal status. The full implementation, including timeouts and failure handling, is in [Parse documents and get results](/cookbook/parse-and-poll).

## Common failures

| Status | Cause                                              |
| ------ | -------------------------------------------------- |
| `401`  | Missing or invalid PAT                             |
| `403`  | Read-only token or insufficient access             |
| `422`  | Required multipart fields are missing or malformed |
| `500`  | Pathway could not stage the Book or its documents  |

Individual file failures and usage-limit checks occur after this request returns. Read the Book and its documents for the final outcome. A usage limit moves the Book to `failed` and places the reason in `parse_status_message`.
