# `DocRaptor`
[🔗](https://github.com/ruskenberry/docraptor/blob/v0.1.2/lib/doc_raptor.ex#L1)

Elixir client for the [DocRaptor](https://docraptor.com) HTML-to-PDF/XLS API.

Built on [Req](https://hex.pm/packages/req). High-level helpers prepare a
`%Req.Request{}` via `DocRaptor.Client.attach/2`, then call `Req.request/1`.

For a compact agent-oriented reference (attrs, errors, sync/async/hosted
choice, pitfalls), see the [Usage](usage.html) guide. Tutorial-oriented
examples live in the [README](readme.html).

## Quick start

    {:ok, client} = DocRaptor.Client.new(api_key: System.fetch_env!("DOCRAPTOR_API_KEY"))
    # test: true by default — watermarked, free quota

    {:ok, %DocRaptor.Document{body: pdf}} =
      DocRaptor.create_doc(client, %{
        name: "hello.pdf",
        document_type: :pdf,
        document_content: "<html><body>Hello</body></html>"
      })

## Document attrs

Create helpers take a map or keyword list (`t:doc_attrs/0`). **Required:**

- `name` — non-empty string
- `document_type` or `type` — present and stringified (DocRaptor expects `pdf` / `xls` / `xlsx`)
- exactly one of `document_content` or `document_url`

Optional `test` overrides the client default. All other keys (including
nested `prince_options`) are passed through to DocRaptor.

Prefer `document_content` (with inlined CSS/assets) over `document_url` when
developing against localhost — DocRaptor cannot fetch private hosts without a
tunnel.

## Returns

Success is always `{:ok, struct}`; failure is `{:error, %DocRaptor.Error{}}`.
There are no bang variants.

# `doc_attrs`

```elixir
@type doc_attrs() :: map() | keyword()
```

Document creation attributes.

Required keys: `name`, `document_type` (or `type`), and exactly one of
`document_content` / `document_url`. See [Usage](usage.html) for the pass-through
parameter list.

# `await_async_doc`

```elixir
@spec await_async_doc(DocRaptor.Client.t(), String.t(), keyword()) ::
  {:ok, DocRaptor.Document.t()} | {:error, DocRaptor.Error.t()}
```

Polls async status until completed or failed, then downloads the document.

Convenient wrapper over `get_async_doc_status/2` + `get_async_doc/2`.
On `:failed`, returns `{:error, %DocRaptor.Error{reason: :async_failed}}`.
On deadline, `reason: :timeout`.

**Blocks the calling process** via `Process.sleep/1` between polls. Prefer an
Oban/worker (or `callback_url`) over calling this inside a Phoenix request.

## Options

- `:interval_ms` — sleep between polls (default `1000`)
- `:timeout_ms` — overall deadline (default `600_000`)

## Examples

    {:ok, %DocRaptor.AsyncJob{status_id: id}} = DocRaptor.create_async_doc(client, attrs)
    {:ok, %DocRaptor.Document{body: pdf}} = DocRaptor.await_async_doc(client, id)

# `create_async_doc`

```elixir
@spec create_async_doc(DocRaptor.Client.t(), doc_attrs()) ::
  {:ok, DocRaptor.AsyncJob.t()} | {:error, DocRaptor.Error.t()}
```

Queues an asynchronous document (`POST /async_docs`).

Returns `%DocRaptor.AsyncJob{status_id: id}`. Poll with
`get_async_doc_status/2`, download with `get_async_doc/2`, or use
`await_async_doc/3`.

# `create_doc`

```elixir
@spec create_doc(DocRaptor.Client.t(), doc_attrs()) ::
  {:ok, DocRaptor.Document.t()} | {:error, DocRaptor.Error.t()}
```

Creates a document synchronously (`POST /docs`).

Returns PDF/XLS bytes as `%DocRaptor.Document{}`. DocRaptor enforces ~60s for
sync jobs; use `create_async_doc/2` for longer work.

## Examples

    DocRaptor.create_doc(client, %{
      name: "hello.pdf",
      document_type: :pdf,
      document_content: "<html><body>Hello</body></html>"
    })

# `create_hosted_async_doc`

```elixir
@spec create_hosted_async_doc(DocRaptor.Client.t(), doc_attrs()) ::
  {:ok, DocRaptor.AsyncJob.t()} | {:error, DocRaptor.Error.t()}
```

Queues a hosted asynchronous document (`POST /hosted_async_docs`).

Returns `%DocRaptor.AsyncJob{}`. When complete, status includes a public
`download_url`. Optional: `callback_url`, hosted limit/expiry attrs.

# `create_hosted_doc`

```elixir
@spec create_hosted_doc(DocRaptor.Client.t(), doc_attrs()) ::
  {:ok, DocRaptor.Status.t()} | {:error, DocRaptor.Error.t()}
```

Creates a hosted document synchronously (`POST /hosted_docs`).

Hosted documents are a DocRaptor paid add-on. Success is
`%DocRaptor.Status{}` with `download_url` / `download_id` (not raw bytes).
Optional attrs: `hosted_download_limit`, `hosted_expires_at`.

# `expire`

```elixir
@spec expire(DocRaptor.Client.t(), String.t()) :: :ok | {:error, DocRaptor.Error.t()}
```

Expires a hosted document (`PATCH /expire/:id`).

`download_id` is the hosted document id from create/status. Returns `:ok` on
success.

# `get_async_doc`

```elixir
@spec get_async_doc(DocRaptor.Client.t(), String.t()) ::
  {:ok, DocRaptor.Document.t()} | {:error, DocRaptor.Error.t()}
```

Downloads a finished async document (`GET /download/:id`).

`download_id` comes from a completed `%DocRaptor.Status{}` (non-hosted).

# `get_async_doc_status`

```elixir
@spec get_async_doc_status(DocRaptor.Client.t(), String.t()) ::
  {:ok, DocRaptor.Status.t()} | {:error, DocRaptor.Error.t()}
```

Checks async document status (`GET /status/:id`).

`status_id` comes from `%DocRaptor.AsyncJob{}`. Status atoms include
`:queued`, `:working`, `:completed`, `:failed`. On completion, use
`download_id` with `get_async_doc/2` (non-hosted) or `download_url` (hosted).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
