Metadata-Version: 2.5
Name: inkrelay
Version: 0.3.0
Summary: Python client for the InkRelay cloud printing API
Project-URL: Repository, https://github.com/GauravBR/inkrelay-sdk-python
Project-URL: Documentation, https://docs.inkrelay.app/sdks/python
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: attrs>=23.1
Requires-Dist: httpx<1,>=0.27
Requires-Dist: python-dateutil>=2.8
Requires-Dist: typing-extensions>=4.7
Description-Content-Type: text/markdown

# inkrelay-sdk-python

Python client for the [InkRelay](https://inkrelay.app) cloud printing API.

## Install

```sh
pip install inkrelay
```

The distribution is named `inkrelay`; the import package is `inkrelay`. Python 3.10 or newer.

## Usage

```python
from inkrelay import InkRelay

ir = InkRelay("ir_test_...")  # or set INKRELAY_API_KEY

job = ir.print_jobs.create_and_wait(
    {
        "printerId": "0192f1a0-7c00-7000-8000-0000000000bb",
        "title": "Invoice 1042",
        "contentType": "pdf_uri",
        "content": "https://example.com/invoice-1042.pdf",
    },
    timeout_s=60,
)
print(job["state"])

for printer in ir.printers.iterate(limit=100):
    print(printer["id"], printer["name"])
```

Resources: `computers`, `printers`, `print_jobs` and `webhooks` have `list`, `iterate` and `get`; `events` has `list` and `iterate`. `print_jobs.create` always sends an Idempotency-Key (a random one unless you pass `idempotency_key=`), so retries never print twice. The client retries 429 responses, and 5xx or network errors for replay safe requests, up to `max_retries` (default 3). Errors raise `InkRelayError` with `status`, `code` and `request_id`.

`ir.raw` is the generated, fully typed client (`inkrelay._generated`) sharing the same retrying connection:

```python
from inkrelay._generated.api.account import get_whoami

who = get_whoami.sync(client=ir.raw)
```

Caveats: only the sync functions on `ir.raw` itself retry. `ir.raw.with_headers(...)` (and `with_cookies`, `with_timeout`) adds the change to the shared client but returns a copy that builds its own httpx client without retries, and the `asyncio` variants use a separate async client that does not retry.

## Async

`AsyncInkRelay` has the same resources as `InkRelay` with awaitable methods; `iterate` is an async generator, and `sleep` (default `asyncio.sleep`) is awaited between retries and polls.

```python
import asyncio
from inkrelay import AsyncInkRelay

async def main():
    async with AsyncInkRelay("ir_test_...") as ir:  # or call `await ir.aclose()`
        job = await ir.print_jobs.create_and_wait(
            {
                "printerId": "0192f1a0-7c00-7000-8000-0000000000bb",
                "title": "Invoice 1042",
                "contentType": "pdf_uri",
                "content": "https://example.com/invoice-1042.pdf",
            }
        )
        print(job["state"])
        async for printer in ir.printers.iterate(limit=100):
            print(printer["id"], printer["name"])

asyncio.run(main())
```

On the async client, `ir.raw` is bound to the same retrying async httpx client, so use the generated `asyncio` and `asyncio_detailed` functions with it (for example `await get_whoami.asyncio(client=ir.raw)`). The same copy caveat applies to `with_headers`, `with_cookies` and `with_timeout`.

## Webhooks

Verify each request with the endpoint's signing secret before trusting it. Pass the raw body, not parsed JSON:

```python
from inkrelay import WebhookVerificationError, webhooks

try:
    webhooks.verify(request.body, request.headers, "whsec_...")
except WebhookVerificationError:
    return 400
```

Rotate an endpoint's signing secret, optionally keeping the old one valid for `overlap_seconds` so receivers can switch, and send a test delivery:

```python
endpoint = ir.webhooks.rotate_secret("0192f1a0-...", overlap_seconds=3600)
print(endpoint["secret"])  # the new secret, shown only once

delivery = ir.webhooks.ping("0192f1a0-...")  # queues a test delivery
```

## Development

```sh
uv sync
uv run openapi-python-client generate --path openapi/openapi.json --config openapi-python-client.yml --meta none --output-path src/inkrelay/_generated --overwrite
uv run ruff check && uv run pytest
uv build
```

`openapi/openapi.json` is synced from inkrelay-api, and `src/inkrelay/_generated` is generated from it; do not edit either by hand. CI regenerates the client on every push and fails a pull request whose generated code is stale.

## Releasing

Set `version` in `pyproject.toml`, merge, then push a matching tag (`git tag v0.2.0 && git push origin v0.2.0`). The `release` workflow checks that the tag matches the version, runs the tests, builds and publishes to PyPI through trusted publishing (GitHub environment `pypi`, PyPI project `inkrelay`).

## License

MIT
